blob: d0b2edadc7141f5023f5722f761653ae490a9452 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.location.provider;
import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationRequest.PASSIVE_INTERVAL;
import static android.location.provider.ProviderProperties.POWER_USAGE_HIGH;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
import static androidx.test.ext.truth.location.LocationSubject.assertThat;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.LocationUtils.createLocation;
import static com.android.server.location.LocationUtils.createLocationResult;
import static com.android.server.location.listeners.RemoteListenerRegistration.IN_PROCESS_EXECUTOR;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.after;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.testng.Assert.assertThrows;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.location.ILocationCallback;
import android.location.ILocationListener;
import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationManagerInternal;
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Bundle;
import android.os.ICancellationSignal;
import android.os.IRemoteCallback;
import android.os.PackageTagsList;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.WorkSource;
import android.platform.test.annotations.Presubmit;
import android.util.Log;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.location.injector.FakeUserInfoHelper;
import com.android.server.location.injector.TestInjector;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class LocationProviderManagerTest {
private static final String TAG = "LocationProviderManagerTest";
private static final long TIMEOUT_MS = 1000;
private static final int CURRENT_USER = FakeUserInfoHelper.DEFAULT_USERID;
private static final int OTHER_USER = CURRENT_USER + 10;
private static final String NAME = "test";
private static final ProviderProperties PROPERTIES = new ProviderProperties.Builder()
.setHasAltitudeSupport(true)
.setHasSpeedSupport(true)
.setHasBearingSupport(true)
.setPowerUsage(POWER_USAGE_HIGH)
.setAccuracy(ProviderProperties.ACCURACY_FINE)
.build();
private static final CallerIdentity PROVIDER_IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1,
"mypackage", "attribution");
private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1,
"mypackage", "attribution", "listener");
private static final WorkSource WORK_SOURCE = new WorkSource(IDENTITY.getUid());
private Random mRandom;
@Mock
private LocationProviderManager.StateChangedListener mStateChangedListener;
@Mock
private LocationManagerInternal mInternal;
@Mock
private Context mContext;
@Mock
private Resources mResources;
@Mock
private PackageManager mPackageManager;
@Mock
private PowerManager mPowerManager;
@Mock
private PowerManager.WakeLock mWakeLock;
private TestInjector mInjector;
private PassiveLocationProviderManager mPassive;
private TestProvider mProvider;
private LocationProviderManager mManager;
@Before
public void setUp() {
initMocks(this);
long seed = System.currentTimeMillis();
Log.i(TAG, "location random seed: " + seed);
mRandom = new Random(seed);
LocalServices.addService(LocationManagerInternal.class, mInternal);
doReturn("android").when(mContext).getPackageName();
doReturn(mResources).when(mContext).getResources();
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
mInjector = new TestInjector(mContext);
mInjector.getUserInfoHelper().startUser(OTHER_USER);
mPassive = new PassiveLocationProviderManager(mContext, mInjector);
mPassive.startManager(null);
mPassive.setRealProvider(new PassiveLocationProvider(mContext));
createManager(NAME);
}
private void createManager(String name) {
mStateChangedListener = mock(LocationProviderManager.StateChangedListener.class);
mProvider = new TestProvider(PROPERTIES, PROVIDER_IDENTITY);
mProvider.setProviderAllowed(true);
mManager = new LocationProviderManager(mContext, mInjector, name, mPassive);
mManager.startManager(mStateChangedListener);
mManager.setRealProvider(mProvider);
}
@After
public void tearDown() throws Exception {
LocalServices.removeServiceForTest(LocationManagerInternal.class);
// some test failures may leave the fg thread stuck, interrupt until we get out of it
CountDownLatch latch = new CountDownLatch(1);
FgThread.getExecutor().execute(latch::countDown);
int count = 0;
while (++count < 10 && !latch.await(10, TimeUnit.MILLISECONDS)) {
FgThread.get().getLooper().getThread().interrupt();
}
}
@Test
public void testProperties() {
assertThat(mManager.getName()).isEqualTo(NAME);
assertThat(mManager.getProperties()).isEqualTo(PROPERTIES);
assertThat(mManager.getIdentity()).isEqualTo(IDENTITY);
assertThat(mManager.hasProvider()).isTrue();
ProviderProperties newProperties = new ProviderProperties.Builder()
.setHasNetworkRequirement(true)
.setHasSatelliteRequirement(true)
.setHasCellRequirement(true)
.setHasMonetaryCost(true)
.setPowerUsage(POWER_USAGE_HIGH)
.setAccuracy(ProviderProperties.ACCURACY_COARSE)
.build();
mProvider.setProperties(newProperties);
assertThat(mManager.getProperties()).isEqualTo(newProperties);
CallerIdentity newIdentity = CallerIdentity.forTest(OTHER_USER, 1, "otherpackage",
"otherattribution");
mProvider.setIdentity(newIdentity);
assertThat(mManager.getIdentity()).isEqualTo(newIdentity);
mManager.setRealProvider(null);
assertThat(mManager.hasProvider()).isFalse();
}
@Test
public void testStateChangedListener() {
mProvider.setExtraAttributionTags(Collections.singleton("extra"));
ArgumentCaptor<AbstractLocationProvider.State> captorOld = ArgumentCaptor.forClass(
AbstractLocationProvider.State.class);
ArgumentCaptor<AbstractLocationProvider.State> captorNew = ArgumentCaptor.forClass(
AbstractLocationProvider.State.class);
verify(mStateChangedListener, timeout(TIMEOUT_MS).times(2)).onStateChanged(eq(NAME),
captorOld.capture(), captorNew.capture());
assertThat(captorOld.getAllValues().get(1).extraAttributionTags).isEmpty();
assertThat(captorNew.getAllValues().get(1).extraAttributionTags).containsExactly("extra");
}
@Test
public void testRemoveProvider() {
mManager.setRealProvider(null);
assertThat(mManager.hasProvider()).isFalse();
}
@Test
public void testIsEnabled() {
assertThat(mManager.isEnabled(CURRENT_USER)).isTrue();
assertThat(mManager.isEnabled(OTHER_USER)).isTrue();
mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
assertThat(mManager.isEnabled(CURRENT_USER)).isFalse();
assertThat(mManager.isEnabled(OTHER_USER)).isTrue();
mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER);
mProvider.setAllowed(false);
assertThat(mManager.isEnabled(CURRENT_USER)).isFalse();
assertThat(mManager.isEnabled(OTHER_USER)).isFalse();
mProvider.setAllowed(true);
assertThat(mManager.isEnabled(CURRENT_USER)).isTrue();
assertThat(mManager.isEnabled(OTHER_USER)).isTrue();
}
@Test
public void testIsEnabledListener() {
ProviderEnabledListener listener = mock(ProviderEnabledListener.class);
mManager.addEnabledListener(listener);
verify(listener, never()).onProviderEnabledChanged(anyString(), anyInt(), anyBoolean());
mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, CURRENT_USER,
false);
mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER);
verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, CURRENT_USER,
true);
mProvider.setAllowed(false);
verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, CURRENT_USER,
false);
verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER,
false);
mProvider.setAllowed(true);
verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, CURRENT_USER,
true);
verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER,
true);
mManager.removeEnabledListener(listener);
mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
verifyNoMoreInteractions(listener);
}
@Test
public void testGetLastLocation_Fine() {
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
Location loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isEqualTo(loc);
}
@Test
public void testGetLastLocation_Coarse() {
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
Location loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
Location coarse = mManager.getLastLocation(new LastLocationRequest.Builder().build(),
IDENTITY, PERMISSION_COARSE);
assertThat(coarse).isNotEqualTo(loc);
assertThat(coarse).isNearby(loc, 5000);
}
@Test
public void testGetLastLocation_Bypass() {
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
assertThat(mManager.getLastLocation(
new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
IDENTITY, PERMISSION_FINE)).isNull();
Location loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isEqualTo(loc);
assertThat(mManager.getLastLocation(
new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
IDENTITY, PERMISSION_FINE)).isEqualTo(
loc);
mProvider.setProviderAllowed(false);
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
assertThat(mManager.getLastLocation(
new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
IDENTITY, PERMISSION_FINE)).isEqualTo(
loc);
loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
assertThat(mManager.getLastLocation(
new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
IDENTITY, PERMISSION_FINE)).isEqualTo(
loc);
mProvider.setProviderAllowed(true);
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
assertThat(mManager.getLastLocation(
new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
IDENTITY, PERMISSION_FINE)).isEqualTo(
loc);
loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isEqualTo(loc);
assertThat(mManager.getLastLocation(
new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
IDENTITY, PERMISSION_FINE)).isEqualTo(
loc);
}
@Test
public void testGetLastLocation_ClearOnMockRemoval() {
MockLocationProvider mockProvider = new MockLocationProvider(PROPERTIES, PROVIDER_IDENTITY,
Collections.emptySet());
mockProvider.setAllowed(true);
mManager.setMockProvider(mockProvider);
Location loc = createLocation(NAME, mRandom);
mockProvider.setProviderLocation(loc);
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isEqualTo(loc);
mManager.setMockProvider(null);
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
}
@Test
public void testInjectLastLocation() {
Location loc1 = createLocation(NAME, mRandom);
mManager.injectLastLocation(loc1, CURRENT_USER);
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isEqualTo(loc1);
Location loc2 = createLocation(NAME, mRandom);
mManager.injectLastLocation(loc2, CURRENT_USER);
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isEqualTo(loc1);
}
@Test
public void testPassive_Listener() throws Exception {
ILocationListener listener = createMockLocationListener();
LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
LocationResult loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
verify(listener).onLocationChanged(eq(loc.asList()), nullable(IRemoteCallback.class));
}
@Test
public void testPassive_LastLocation() {
Location loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
assertThat(mPassive.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isEqualTo(loc);
}
@Test
public void testRegisterListener() throws Exception {
ILocationListener listener = createMockLocationListener();
mManager.registerLocationRequest(
new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
IDENTITY,
PERMISSION_FINE,
listener);
LocationResult loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
verify(listener).onLocationChanged(eq(loc.asList()), nullable(IRemoteCallback.class));
mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, false);
loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
verify(listener, times(1)).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER);
verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, true);
mProvider.setAllowed(false);
verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, false);
loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
verify(listener, times(1)).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
mProvider.setAllowed(true);
verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, true);
loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
verify(listener).onLocationChanged(eq(loc.asList()), nullable(IRemoteCallback.class));
}
@Test
public void testRegisterListener_SameProcess() throws Exception {
CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage",
"attribution", "listener");
ILocationListener listener = createMockLocationListener();
mManager.registerLocationRequest(
new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
identity,
PERMISSION_FINE,
listener);
LocationResult loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
verify(listener, timeout(TIMEOUT_MS).times(1)).onLocationChanged(eq(loc.asList()),
nullable(IRemoteCallback.class));
}
@Test
public void testRegisterListener_Unregister() throws Exception {
ILocationListener listener = createMockLocationListener();
mManager.registerLocationRequest(
new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
IDENTITY,
PERMISSION_FINE,
listener);
mManager.unregisterLocationRequest(listener);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
verify(listener, after(TIMEOUT_MS).never()).onProviderEnabledChanged(NAME, false);
}
@Test
public void testRegisterListener_Unregister_SameProcess() throws Exception {
CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage",
"attribution", "listener");
ILocationListener listener = createMockLocationListener();
mManager.registerLocationRequest(
new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
identity,
PERMISSION_FINE,
listener);
CountDownLatch blocker = new CountDownLatch(1);
IN_PROCESS_EXECUTOR.execute(() -> {
try {
blocker.await();
} catch (InterruptedException e) {
// do nothing
}
});
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mManager.unregisterLocationRequest(listener);
blocker.countDown();
verify(listener, after(TIMEOUT_MS).never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@Test
public void testRegisterListener_NumUpdates() throws Exception {
ILocationListener listener = createMockLocationListener();
LocationRequest request = new LocationRequest.Builder(0)
.setMaxUpdates(5)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, times(5)).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@Test
public void testRegisterListener_ExpiringAlarm() throws Exception {
ILocationListener listener = createMockLocationListener();
LocationRequest request = new LocationRequest.Builder(0)
.setDurationMillis(5000)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
mInjector.getAlarmHelper().incrementAlarmTime(5000);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@Test
public void testRegisterListener_ExpiringNoAlarm() throws Exception {
ILocationListener listener = createMockLocationListener();
LocationRequest request = new LocationRequest.Builder(0)
.setDurationMillis(25)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
Thread.sleep(25);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@Test
public void testRegisterListener_FastestInterval() throws Exception {
ILocationListener listener = createMockLocationListener();
LocationRequest request = new LocationRequest.Builder(5000)
.setMinUpdateIntervalMillis(5000)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, times(1)).onLocationChanged(
any(List.class), nullable(IRemoteCallback.class));
}
@Test
public void testRegisterListener_SmallestDisplacement() throws Exception {
ILocationListener listener = createMockLocationListener();
LocationRequest request = new LocationRequest.Builder(5000)
.setMinUpdateDistanceMeters(1f)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
Location loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
mProvider.setProviderLocation(loc);
verify(listener, times(1)).onLocationChanged(
any(List.class), nullable(IRemoteCallback.class));
}
@Test
public void testRegisterListener_NoteOpFailure() throws Exception {
ILocationListener listener = createMockLocationListener();
LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
mInjector.getAppOpsHelper().setAppOpAllowed(OP_FINE_LOCATION, IDENTITY.getPackageName(),
false);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@Test
public void testRegisterListener_Wakelock() throws Exception {
CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage",
"attribution", "listener");
ILocationListener listener = createMockLocationListener();
mManager.registerLocationRequest(
new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
identity,
PERMISSION_FINE,
listener);
CountDownLatch blocker = new CountDownLatch(1);
IN_PROCESS_EXECUTOR.execute(() -> {
try {
blocker.await();
} catch (InterruptedException e) {
// do nothing
}
});
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(mWakeLock).acquire(anyLong());
verify(mWakeLock, never()).release();
blocker.countDown();
verify(listener, timeout(TIMEOUT_MS)).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
verify(mWakeLock).acquire(anyLong());
verify(mWakeLock, timeout(TIMEOUT_MS)).release();
}
@Test
public void testRegisterListener_Coarse() throws Exception {
ILocationListener listener = createMockLocationListener();
mManager.registerLocationRequest(
new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
IDENTITY,
PERMISSION_COARSE,
listener);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, times(1))
.onLocationChanged(any(List.class), nullable(IRemoteCallback.class));
}
@Test
public void testRegisterListener_Coarse_Passive() throws Exception {
ILocationListener listener = createMockLocationListener();
mManager.registerLocationRequest(
new LocationRequest.Builder(PASSIVE_INTERVAL)
.setMinUpdateIntervalMillis(0)
.setWorkSource(WORK_SOURCE).build(),
IDENTITY,
PERMISSION_COARSE,
listener);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, times(1))
.onLocationChanged(any(List.class), nullable(IRemoteCallback.class));
}
@Test
public void testProviderRequestListener() throws Exception {
IProviderRequestListener requestListener = mock(IProviderRequestListener.class);
mManager.addProviderRequestListener(requestListener);
ILocationListener locationListener = createMockLocationListener();
LocationRequest request = new LocationRequest.Builder(1).setWorkSource(
WORK_SOURCE).build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, locationListener);
verify(requestListener, timeout(TIMEOUT_MS).times(1)).onProviderRequestChanged(anyString(),
any(ProviderRequest.class));
mManager.unregisterLocationRequest(locationListener);
mManager.removeProviderRequestListener(requestListener);
}
@Test
public void testGetCurrentLocation() throws Exception {
ILocationCallback listener = createMockGetCurrentLocationListener();
LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
Location loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, times(1)).onLocation(loc);
}
@Test
public void testGetCurrentLocation_Cancel() throws Exception {
ILocationCallback listener = createMockGetCurrentLocationListener();
LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
ICancellationSignal cancellationSignal = mManager.getCurrentLocation(request,
IDENTITY, PERMISSION_FINE, listener);
cancellationSignal.cancel();
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, never()).onLocation(nullable(Location.class));
}
@Test
public void testGetCurrentLocation_ProviderDisabled() throws Exception {
ILocationCallback listener = createMockGetCurrentLocationListener();
LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
mProvider.setProviderAllowed(false);
mProvider.setProviderAllowed(true);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, times(1)).onLocation(isNull());
}
@Test
public void testGetCurrentLocation_ProviderAlreadyDisabled() throws Exception {
mProvider.setProviderAllowed(false);
ILocationCallback listener = createMockGetCurrentLocationListener();
LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
mProvider.setProviderAllowed(true);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, times(1)).onLocation(isNull());
}
@Test
public void testGetCurrentLocation_LastLocation() throws Exception {
Location loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
ILocationCallback listener = createMockGetCurrentLocationListener();
LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
verify(listener, times(1)).onLocation(eq(loc));
}
@Test
public void testGetCurrentLocation_Timeout() throws Exception {
ILocationCallback listener = createMockGetCurrentLocationListener();
LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
mInjector.getAlarmHelper().incrementAlarmTime(60000);
verify(listener, times(1)).onLocation(isNull());
}
@Test
public void testFlush() throws Exception {
ILocationListener listener = createMockLocationListener();
mManager.registerLocationRequest(
new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
IDENTITY,
PERMISSION_FINE,
listener);
mManager.flush(listener, 99);
LocationResult loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
mProvider.completeFlushes();
InOrder inOrder = inOrder(listener);
inOrder.verify(listener).onLocationChanged(eq(loc.asList()), any(IRemoteCallback.class));
inOrder.verify(listener).onFlushComplete(99);
}
@Test
public void testFlush_UnknownKey() {
assertThrows(IllegalArgumentException.class,
() -> mManager.flush(createMockLocationListener(), 0));
}
@Test
public void testLocationMonitoring() {
assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
IDENTITY.getPackageName())).isFalse();
assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
IDENTITY.getPackageName())).isFalse();
ILocationListener listener = createMockLocationListener();
LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
IDENTITY.getPackageName())).isTrue();
assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
IDENTITY.getPackageName())).isTrue();
mInjector.getAppForegroundHelper().setAppForeground(IDENTITY.getUid(), false);
assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
IDENTITY.getPackageName())).isTrue();
assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
IDENTITY.getPackageName())).isFalse();
mManager.unregisterLocationRequest(listener);
assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
IDENTITY.getPackageName())).isFalse();
assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
IDENTITY.getPackageName())).isFalse();
}
@Test
public void testProviderRequest() {
assertThat(mProvider.getRequest().isActive()).isFalse();
ILocationListener listener1 = createMockLocationListener();
LocationRequest request1 = new LocationRequest.Builder(5).setWorkSource(
WORK_SOURCE).build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
assertThat(mProvider.getRequest().isActive()).isTrue();
assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
assertThat(mProvider.getRequest().isLowPower()).isFalse();
assertThat(mProvider.getRequest().getWorkSource()).isNotNull();
ILocationListener listener2 = createMockLocationListener();
LocationRequest request2 = new LocationRequest.Builder(1)
.setLowPower(true)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
assertThat(mProvider.getRequest().isActive()).isTrue();
assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
assertThat(mProvider.getRequest().isLowPower()).isFalse();
assertThat(mProvider.getRequest().getWorkSource()).isNotNull();
mManager.unregisterLocationRequest(listener1);
assertThat(mProvider.getRequest().isActive()).isTrue();
assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
assertThat(mProvider.getRequest().isLowPower()).isTrue();
assertThat(mProvider.getRequest().getWorkSource()).isNotNull();
mManager.unregisterLocationRequest(listener2);
assertThat(mProvider.getRequest().isActive()).isFalse();
}
@Test
public void testProviderRequest_DelayedRequest() throws Exception {
mProvider.setProviderLocation(createLocation(NAME, mRandom));
ILocationListener listener1 = createMockLocationListener();
LocationRequest request1 = new LocationRequest.Builder(60000)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
verify(listener1).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
assertThat(mProvider.getRequest().isActive()).isFalse();
mInjector.getAlarmHelper().incrementAlarmTime(60000);
assertThat(mProvider.getRequest().isActive()).isTrue();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(60000);
}
@Test
public void testProviderRequest_DelayedRequest_Remove() {
mProvider.setProviderLocation(createLocation(NAME, mRandom));
ILocationListener listener1 = createMockLocationListener();
LocationRequest request1 = new LocationRequest.Builder(60000)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
mManager.unregisterLocationRequest(listener1);
mInjector.getAlarmHelper().incrementAlarmTime(60000);
assertThat(mProvider.getRequest().isActive()).isFalse();
}
@Test
public void testProviderRequest_SpamRequesting() {
mProvider.setProviderLocation(createLocation(NAME, mRandom));
ILocationListener listener1 = createMockLocationListener();
LocationRequest request1 = new LocationRequest.Builder(60000)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
assertThat(mProvider.getRequest().isActive()).isFalse();
mManager.unregisterLocationRequest(listener1);
assertThat(mProvider.getRequest().isActive()).isFalse();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
assertThat(mProvider.getRequest().isActive()).isFalse();
mManager.unregisterLocationRequest(listener1);
assertThat(mProvider.getRequest().isActive()).isFalse();
}
@Test
public void testProviderRequest_BackgroundThrottle() {
ILocationListener listener1 = createMockLocationListener();
LocationRequest request1 = new LocationRequest.Builder(5)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
mInjector.getAppForegroundHelper().setAppForeground(IDENTITY.getUid(), false);
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(
mInjector.getSettingsHelper().getBackgroundThrottleIntervalMs());
}
@Test
public void testProviderRequest_IgnoreLocationSettings() {
mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
new PackageTagsList.Builder().add(
IDENTITY.getPackageName()).build());
ILocationListener listener1 = createMockLocationListener();
LocationRequest request1 = new LocationRequest.Builder(5)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
assertThat(mProvider.getRequest().isActive()).isTrue();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
ILocationListener listener2 = createMockLocationListener();
LocationRequest request2 = new LocationRequest.Builder(1)
.setLocationSettingsIgnored(true)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
assertThat(mProvider.getRequest().isActive()).isTrue();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isTrue();
}
@Test
public void testProviderRequest_IgnoreLocationSettings_ProviderDisabled() {
mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
new PackageTagsList.Builder().add(
IDENTITY.getPackageName()).build());
ILocationListener listener1 = createMockLocationListener();
LocationRequest request1 = new LocationRequest.Builder(1)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
ILocationListener listener2 = createMockLocationListener();
LocationRequest request2 = new LocationRequest.Builder(5)
.setLocationSettingsIgnored(true)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
assertThat(mProvider.getRequest().isActive()).isTrue();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isTrue();
}
@Test
public void testProviderRequest_IgnoreLocationSettings_NoAllowlist() {
mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
new PackageTagsList.Builder().add(
IDENTITY.getPackageName()).build());
ILocationListener listener = createMockLocationListener();
LocationRequest request = new LocationRequest.Builder(1)
.setLocationSettingsIgnored(true)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
new PackageTagsList.Builder().build());
assertThat(mProvider.getRequest().isActive()).isTrue();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
}
@Test
public void testProviderRequest_BackgroundThrottle_IgnoreLocationSettings() {
mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
new PackageTagsList.Builder().add(
IDENTITY.getPackageName()).build());
ILocationListener listener1 = createMockLocationListener();
LocationRequest request1 = new LocationRequest.Builder(5)
.setLocationSettingsIgnored(true)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
mInjector.getAppForegroundHelper().setAppForeground(IDENTITY.getUid(), false);
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
}
@Test
public void testProviderRequest_AdasGnssBypass() {
doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
createManager(GPS_PROVIDER);
ILocationListener listener1 = createMockLocationListener();
LocationRequest request1 = new LocationRequest.Builder(5)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
assertThat(mProvider.getRequest().isActive()).isTrue();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
assertThat(mProvider.getRequest().isAdasGnssBypass()).isFalse();
ILocationListener listener2 = createMockLocationListener();
LocationRequest request2 = new LocationRequest.Builder(1)
.setAdasGnssBypass(true)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
assertThat(mProvider.getRequest().isActive()).isTrue();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
assertThat(mProvider.getRequest().isAdasGnssBypass()).isTrue();
}
@Test
public void testProviderRequest_AdasGnssBypass_ProviderDisabled() {
doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
createManager(GPS_PROVIDER);
ILocationListener listener1 = createMockLocationListener();
LocationRequest request1 = new LocationRequest.Builder(1)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
ILocationListener listener2 = createMockLocationListener();
LocationRequest request2 = new LocationRequest.Builder(5)
.setAdasGnssBypass(true)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
assertThat(mProvider.getRequest().isActive()).isTrue();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
assertThat(mProvider.getRequest().isAdasGnssBypass()).isTrue();
}
@Test
public void testProviderRequest_AdasGnssBypass_ProviderDisabled_AdasDisabled() {
mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
new PackageTagsList.Builder().add(
IDENTITY.getPackageName()).build());
doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
createManager(GPS_PROVIDER);
ILocationListener listener1 = createMockLocationListener();
LocationRequest request1 = new LocationRequest.Builder(5)
.setLocationSettingsIgnored(true)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
ILocationListener listener2 = createMockLocationListener();
LocationRequest request2 = new LocationRequest.Builder(1)
.setAdasGnssBypass(true)
.setWorkSource(WORK_SOURCE)
.build();
mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
mInjector.getLocationSettings().updateUserSettings(IDENTITY.getUserId(),
settings -> settings.withAdasGnssLocationEnabled(false));
mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
assertThat(mProvider.getRequest().isActive()).isTrue();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
assertThat(mProvider.getRequest().isAdasGnssBypass()).isFalse();
}
@Test
public void testProviderRequest_BatterySaver_ScreenOnOff() {
mInjector.getLocationPowerSaveModeHelper().setLocationPowerSaveMode(
LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
ILocationListener listener = createMockLocationListener();
LocationRequest request = new LocationRequest.Builder(5).setWorkSource(WORK_SOURCE).build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
assertThat(mProvider.getRequest().isActive()).isTrue();
mInjector.getScreenInteractiveHelper().setScreenInteractive(false);
assertThat(mProvider.getRequest().isActive()).isFalse();
}
private ILocationListener createMockLocationListener() {
return spy(new ILocationListener.Stub() {
@Override
public void onLocationChanged(List<Location> locations,
IRemoteCallback onCompleteCallback) {
if (onCompleteCallback != null) {
try {
onCompleteCallback.sendResult(null);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
}
@Override
public void onFlushComplete(int requestCode) {
}
@Override
public void onProviderEnabledChanged(String provider, boolean enabled) {
}
});
}
private ILocationCallback createMockGetCurrentLocationListener() {
return spy(new ILocationCallback.Stub() {
@Override
public void onLocation(Location location) {
}
});
}
private static class TestProvider extends AbstractLocationProvider {
private ProviderRequest mProviderRequest = ProviderRequest.EMPTY_REQUEST;
private final ArrayList<Runnable> mFlushCallbacks = new ArrayList<>();
TestProvider(ProviderProperties properties, CallerIdentity identity) {
super(DIRECT_EXECUTOR, identity, properties, Collections.emptySet());
}
public void setProviderAllowed(boolean allowed) {
setAllowed(allowed);
}
public void setProviderLocation(Location l) {
reportLocation(LocationResult.create(new Location(l)));
}
public void setProviderLocation(LocationResult l) {
reportLocation(l);
}
public void completeFlushes() {
for (Runnable r : mFlushCallbacks) {
r.run();
}
mFlushCallbacks.clear();
}
public ProviderRequest getRequest() {
return mProviderRequest;
}
@Override
public void onSetRequest(ProviderRequest request) {
mProviderRequest = request;
}
@Override
protected void onFlush(Runnable callback) {
mFlushCallbacks.add(callback);
}
@Override
protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
}
}
}