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.location.fused.tests;
18 
19 import static android.location.LocationManager.GPS_PROVIDER;
20 import static android.location.LocationManager.NETWORK_PROVIDER;
21 
22 import static androidx.test.ext.truth.location.LocationSubject.assertThat;
23 
24 import android.content.Context;
25 import android.location.Criteria;
26 import android.location.Location;
27 import android.location.LocationManager;
28 import android.location.LocationRequest;
29 import android.location.provider.ILocationProvider;
30 import android.location.provider.ILocationProviderManager;
31 import android.location.provider.ProviderProperties;
32 import android.location.provider.ProviderRequest;
33 import android.os.ParcelFileDescriptor;
34 import android.os.SystemClock;
35 import android.util.Log;
36 
37 import androidx.test.InstrumentationRegistry;
38 import androidx.test.core.app.ApplicationProvider;
39 import androidx.test.runner.AndroidJUnit4;
40 
41 import com.android.location.fused.FusedLocationProvider;
42 
43 import org.junit.After;
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 
48 import java.io.ByteArrayOutputStream;
49 import java.io.FileInputStream;
50 import java.io.IOException;
51 import java.util.List;
52 import java.util.Random;
53 import java.util.concurrent.LinkedBlockingQueue;
54 import java.util.concurrent.TimeUnit;
55 
56 @RunWith(AndroidJUnit4.class)
57 public class FusedLocationServiceTest {
58 
59     private static final String TAG = "FusedLocationServiceTest";
60 
61     private static final long TIMEOUT_MS = 5000;
62 
63     private Random mRandom;
64     private LocationManager mLocationManager;
65 
66     private ILocationProvider mProvider;
67     private LocationProviderManagerCapture mManager;
68 
69     @Before
setUp()70     public void setUp() throws Exception {
71         long seed = System.currentTimeMillis();
72         Log.i(TAG, "location seed: " + seed);
73 
74         Context context = ApplicationProvider.getApplicationContext();
75         mRandom = new Random(seed);
76         mLocationManager = context.getSystemService(LocationManager.class);
77 
78         setMockLocation(true);
79 
80         mManager = new LocationProviderManagerCapture();
81         mProvider = ILocationProvider.Stub.asInterface(
82                 new FusedLocationProvider(context).getBinder());
83         mProvider.setLocationProviderManager(mManager);
84 
85         mLocationManager.addTestProvider(NETWORK_PROVIDER,
86                 true,
87                 false,
88                 true,
89                 false,
90                 false,
91                 false,
92                 false,
93                 Criteria.POWER_MEDIUM,
94                 Criteria.ACCURACY_FINE);
95         mLocationManager.setTestProviderEnabled(NETWORK_PROVIDER, true);
96         mLocationManager.addTestProvider(GPS_PROVIDER,
97                 true,
98                 false,
99                 true,
100                 false,
101                 false,
102                 false,
103                 false,
104                 Criteria.POWER_MEDIUM,
105                 Criteria.ACCURACY_FINE);
106         mLocationManager.setTestProviderEnabled(GPS_PROVIDER, true);
107     }
108 
109     @After
tearDown()110     public void tearDown() throws Exception {
111         for (String provider : mLocationManager.getAllProviders()) {
112             mLocationManager.removeTestProvider(provider);
113         }
114 
115         setMockLocation(false);
116     }
117 
118     @Test
testNetworkRequest()119     public void testNetworkRequest() throws Exception {
120         mProvider.setRequest(
121                         new ProviderRequest.Builder()
122                                 .setIntervalMillis(1000)
123                                 .build());
124 
125         Location location = createLocation(NETWORK_PROVIDER, mRandom);
126         mLocationManager.setTestProviderLocation(NETWORK_PROVIDER, location);
127 
128         assertThat(mManager.getNextLocation(TIMEOUT_MS)).isEqualTo(location);
129     }
130 
131     @Test
testGpsRequest()132     public void testGpsRequest() throws Exception {
133         mProvider.setRequest(
134                 new ProviderRequest.Builder()
135                         .setQuality(LocationRequest.QUALITY_HIGH_ACCURACY)
136                         .setIntervalMillis(1000)
137                         .build());
138 
139         Location location = createLocation(GPS_PROVIDER, mRandom);
140         mLocationManager.setTestProviderLocation(GPS_PROVIDER, location);
141 
142         assertThat(mManager.getNextLocation(TIMEOUT_MS)).isEqualTo(location);
143     }
144 
145     private static class LocationProviderManagerCapture extends ILocationProviderManager.Stub {
146 
147         private final LinkedBlockingQueue<Location> mLocations;
148 
LocationProviderManagerCapture()149         private LocationProviderManagerCapture() {
150             mLocations = new LinkedBlockingQueue<>();
151         }
152 
153         @Override
onInitialize(boolean allowed, ProviderProperties properties, String attributionTag)154         public void onInitialize(boolean allowed, ProviderProperties properties,
155                 String attributionTag) {}
156 
157         @Override
onSetAllowed(boolean allowed)158         public void onSetAllowed(boolean allowed) {}
159 
160         @Override
onSetProperties(ProviderProperties properties)161         public void onSetProperties(ProviderProperties properties) {}
162 
163         @Override
onReportLocation(Location location)164         public void onReportLocation(Location location) {
165             mLocations.add(location);
166         }
167 
168         @Override
onReportLocations(List<Location> locations)169         public void onReportLocations(List<Location> locations) {
170             mLocations.addAll(locations);
171         }
172 
173         @Override
onFlushComplete()174         public void onFlushComplete() {}
175 
getNextLocation(long timeoutMs)176         public Location getNextLocation(long timeoutMs) throws InterruptedException {
177             return mLocations.poll(timeoutMs, TimeUnit.MILLISECONDS);
178         }
179     }
180 
181     private static final double MIN_LATITUDE = -90D;
182     private static final double MAX_LATITUDE = 90D;
183     private static final double MIN_LONGITUDE = -180D;
184     private static final double MAX_LONGITUDE = 180D;
185 
186     private static final float MIN_ACCURACY = 1;
187     private static final float MAX_ACCURACY = 100;
188 
createLocation(String provider, Random random)189     private static Location createLocation(String provider, Random random) {
190         return createLocation(provider,
191                 MIN_LATITUDE + random.nextDouble() * (MAX_LATITUDE - MIN_LATITUDE),
192                 MIN_LONGITUDE + random.nextDouble() * (MAX_LONGITUDE - MIN_LONGITUDE),
193                 MIN_ACCURACY + random.nextFloat() * (MAX_ACCURACY - MIN_ACCURACY));
194     }
195 
createLocation(String provider, double latitude, double longitude, float accuracy)196     private static Location createLocation(String provider, double latitude, double longitude,
197             float accuracy) {
198         Location location = new Location(provider);
199         location.setLatitude(latitude);
200         location.setLongitude(longitude);
201         location.setAccuracy(accuracy);
202         location.setTime(System.currentTimeMillis());
203         location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
204         return location;
205     }
206 
setMockLocation(boolean allowed)207     private static void setMockLocation(boolean allowed) throws IOException {
208         ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
209                 .executeShellCommand("appops set "
210                         + InstrumentationRegistry.getTargetContext().getPackageName()
211                         + " android:mock_location " + (allowed ? "allow" : "deny"));
212         try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
213             ByteArrayOutputStream os = new ByteArrayOutputStream();
214             byte[] buffer = new byte[32768];
215             int count;
216             try {
217                 while ((count = fis.read(buffer)) != -1) {
218                     os.write(buffer, 0, count);
219                 }
220                 fis.close();
221             } catch (IOException e) {
222                 throw new RuntimeException(e);
223             }
224             Log.e(TAG, new String(os.toByteArray()));
225         }
226     }
227 }
228