1 /*
2  * Copyright (C) 2022 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.bedstead.nene.location;
18 
19 import static android.app.AppOpsManager.OPSTR_MOCK_LOCATION;
20 
21 import static com.android.bedstead.nene.appops.AppOpsMode.DEFAULT;
22 
23 import android.app.AppOpsManager;
24 import android.content.Context;
25 import android.location.Location;
26 import android.location.LocationManager;
27 import android.location.provider.ProviderProperties;
28 import android.os.SystemClock;
29 
30 import com.android.bedstead.nene.TestApis;
31 import com.android.bedstead.permissions.PermissionContext;
32 
33 /** A test location provider on the device. */
34 public final class LocationProvider implements AutoCloseable {
35 
36     private static final Context sContext = TestApis.context().instrumentedContext();
37     private static final LocationManager sLocationManager = sContext.getSystemService(
38             LocationManager.class);
39     private final String sProviderName;
40 
LocationProvider(String provider)41     LocationProvider(String provider) {
42         this.sProviderName = provider;
43         addAndEnableTestProvider();
44     }
45 
46     @Override
close()47     public void close() {
48         removeTestProvider();
49         clearTestProviderLocation();
50     }
51 
addAndEnableTestProvider()52     private void addAndEnableTestProvider() {
53         try (PermissionContext p = TestApis.permissions().withAppOp(OPSTR_MOCK_LOCATION)) {
54             addTestProvider();
55             enableTestProvider();
56         }
57     }
58 
addTestProvider()59     private void addTestProvider() {
60         sLocationManager.addTestProvider(sProviderName,
61                 /* requiresNetwork= */ true,
62                 /* requiresSatellite= */ false,
63                 /* requiresCell= */ true,
64                 /* hasMonetaryCost= */ false,
65                 /* supportsAltitude= */ false,
66                 /* supportsSpeed= */ false,
67                 /* supportsBearing= */ false,
68                 ProviderProperties.POWER_USAGE_MEDIUM,
69                 ProviderProperties.ACCURACY_COARSE);
70     }
71 
enableTestProvider()72     private void enableTestProvider() {
73         sLocationManager.setTestProviderEnabled(sProviderName, /* enabled= */true);
74     }
75 
removeTestProvider()76     private void removeTestProvider() {
77         try (PermissionContext p = TestApis.permissions().withAppOp(OPSTR_MOCK_LOCATION)) {
78             sLocationManager.removeTestProvider(sProviderName);
79         }
80         TestApis.packages().instrumented().appOps().set(AppOpsManager.OPSTR_MOCK_LOCATION, DEFAULT);
81     }
82 
clearTestProviderLocation()83     private void clearTestProviderLocation() {
84         // This is a work-around for removing the test provider's location.
85         // There is no LocationManager test API to do this.
86         TestApis.location().setLocationEnabled(false);
87         TestApis.location().setLocationEnabled(true);
88     }
89 
setLocation(double latitude, double longitude, float accuracy)90     public void setLocation(double latitude, double longitude, float accuracy) {
91         sLocationManager.setTestProviderLocation(sProviderName,
92                 createLocation(sProviderName, latitude, longitude, accuracy));
93     }
94 
createLocation(String provider, double latitude, double longitude, float accuracy)95     private static Location createLocation(String provider, double latitude, double longitude,
96             float accuracy) {
97         Location location = new Location(provider);
98         location.setLatitude(latitude);
99         location.setLongitude(longitude);
100         location.setAccuracy(accuracy);
101         location.setTime(System.currentTimeMillis());
102         location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
103         return location;
104     }
105 }
106