1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cts.verifier.managedprovisioning;
18 
19 import android.Manifest;
20 import android.app.Activity;
21 import android.content.Intent;
22 import android.content.pm.PackageManager;
23 import android.location.Location;
24 import android.location.LocationManager;
25 import android.location.LocationRequest;
26 import android.os.Bundle;
27 import android.os.CancellationSignal;
28 import android.util.Log;
29 import android.widget.TextView;
30 
31 import androidx.annotation.NonNull;
32 import androidx.core.app.ActivityCompat;
33 import androidx.core.content.ContextCompat;
34 
35 import com.android.cts.verifier.R;
36 
37 import java.util.List;
38 import java.util.stream.Collectors;
39 
40 /**
41  * Activity that checks whether it can obtain location. Asks for permission in case it is missing.
42  * Result is reported to whoever invoked it with startActivityForResult.
43  */
44 public class LocationCheckerActivity extends Activity {
45     private static final String TAG = LocationCheckerActivity.class.getSimpleName();
46 
47     public static final String ACTION_CHECK_LOCATION_PRIMARY =
48             "com.android.cts.verifier.managedprovisioning.CHECK_LOCATION_ACCESS_PRIMARY";
49     public static final String ACTION_CHECK_LOCATION_WORK =
50             "com.android.cts.verifier.managedprovisioning.CHECK_LOCATION_ACCESS_WORK";
51     public static final String WORK_ACTIVITY_ALIAS =
52             "com.android.cts.verifier.managedprovisioning.WorkLocationCheckerActivityAlias";
53 
54     public static final String EXTRA_ERROR_ID = "extra-error-id";
55     public static final int LOCATION_PERMISSION_REQUEST = 125;
56     private static final int REQUEST_MAX_DURATION_MILLIS = 15000;
57 
58     private List<String> mProviders;
59     private LocationManager mLocationManager;
60     private TextView mStatusText;
61     private final CancellationSignal mCancellationSignal = new CancellationSignal();
62 
63     @Override
onCreate(Bundle savedInstanceState)64     protected void onCreate(Bundle savedInstanceState) {
65         super.onCreate(savedInstanceState);
66         setContentView(R.layout.location_checker);
67 
68         mLocationManager = getSystemService(LocationManager.class);
69         mStatusText = findViewById(R.id.status_text);
70 
71         if (hasLocationPermission()) {
72             requestCurrentLocation();
73         } else {
74             requestLocationPermission();
75         }
76     }
77 
requestCurrentLocation()78     private void requestCurrentLocation() {
79         synchronized (this) {
80             mProviders = mLocationManager.getAllProviders().stream()
81                     .filter(mLocationManager::isProviderEnabled)
82                     .collect(Collectors.toList());
83             if (mProviders.isEmpty()) {
84                 finish(R.string.provisioning_byod_location_no_provider);
85                 return;
86             }
87             // Callback will be invoked with null if duration exceeded.
88             LocationRequest request = new LocationRequest.Builder(0)
89                     .setDurationMillis(REQUEST_MAX_DURATION_MILLIS).build();
90             // Request location from all available providers.
91             for (String provider: mProviders) {
92                 mLocationManager.getCurrentLocation(provider, request, mCancellationSignal,
93                         getMainExecutor(), l -> onLocation(provider, l));
94             }
95             updateStatusLocked();
96         }
97     }
98 
updateStatusLocked()99     private void updateStatusLocked() {
100         String providers = String.join(", ", mProviders);
101         mStatusText.setText(getString(R.string.provisioning_byod_location_trying, providers));
102     }
103 
hasLocationPermission()104     private boolean hasLocationPermission() {
105         return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
106                 == PackageManager.PERMISSION_GRANTED;
107     }
108 
requestLocationPermission()109     private void requestLocationPermission() {
110         ActivityCompat.requestPermissions(this,
111                 new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
112                 LOCATION_PERMISSION_REQUEST);
113     }
114 
115     @Override
onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grants)116     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
117             @NonNull int[] grants) {
118         // Test that the right permission was granted.
119         if (requestCode == LOCATION_PERMISSION_REQUEST) {
120             if (!permissions[0].equals(Manifest.permission.ACCESS_FINE_LOCATION)
121                     || grants[0] != PackageManager.PERMISSION_GRANTED
122                     || !hasLocationPermission()) {
123                 Log.e(TAG, "The test needs location permission.");
124                 finish(R.string.provisioning_byod_location_mode_enable_missing_permission);
125                 return;
126             }
127             requestCurrentLocation();
128         }
129     }
130 
onLocation(String provider, Location location)131     private void onLocation(String provider, Location location) {
132         if (mCancellationSignal.isCanceled()) {
133             return;
134         } else if (location != null) {
135             mCancellationSignal.cancel();
136             finish(-1 /* no error */);
137         }
138 
139         // location == null, provider wasn't able to get location, see if there are more providers
140         synchronized (this) {
141             mProviders.remove(provider);
142             if (mProviders.isEmpty()) {
143                 finish(R.string.provisioning_byod_location_failed);
144             } else {
145                 updateStatusLocked();
146             }
147         }
148     }
149 
finish(int messageId)150     void finish(int messageId) {
151         Intent result = new Intent();
152         result.putExtra(EXTRA_ERROR_ID, messageId);
153         setResult(Activity.RESULT_OK, result);
154         finish();
155     }
156 }
157