Merge "Use PerUserCarService in the CarLocationService." into qt-dev
diff --git a/car-lib/src/android/car/ICarUserService.aidl b/car-lib/src/android/car/ICarUserService.aidl
index 1c7211c..82eab72 100644
--- a/car-lib/src/android/car/ICarUserService.aidl
+++ b/car-lib/src/android/car/ICarUserService.aidl
@@ -16,8 +16,10 @@
package android.car;
import android.car.ICarBluetoothUserService;
+import android.car.ILocationManagerProxy;
/** @hide */
interface ICarUserService {
ICarBluetoothUserService getBluetoothUserService();
+ ILocationManagerProxy getLocationManagerProxy();
}
diff --git a/car-lib/src/android/car/ILocationManagerProxy.aidl b/car-lib/src/android/car/ILocationManagerProxy.aidl
new file mode 100644
index 0000000..5fef542
--- /dev/null
+++ b/car-lib/src/android/car/ILocationManagerProxy.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 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 android.car;
+import android.location.Location;
+
+/** @hide */
+interface ILocationManagerProxy {
+ boolean isLocationEnabled();
+ boolean injectLocation(in Location location);
+ Location getLastKnownLocation(in String provider);
+}
\ No newline at end of file
diff --git a/service/src/com/android/car/CarLocationService.java b/service/src/com/android/car/CarLocationService.java
index cc57856..3827c22 100644
--- a/service/src/com/android/car/CarLocationService.java
+++ b/service/src/com/android/car/CarLocationService.java
@@ -16,6 +16,9 @@
package com.android.car;
+import android.app.ActivityManager;
+import android.car.ICarUserService;
+import android.car.ILocationManagerProxy;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.ICarDrivingStateChangeListener;
import android.car.hardware.power.CarPowerManager;
@@ -30,6 +33,7 @@
import android.location.LocationManager;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.AtomicFile;
@@ -69,6 +73,9 @@
// Used internally for mHandlerThread synchronization
private final Object mLock = new Object();
+ // Used internally for mILocationManagerProxy synchronization
+ private final Object mLocationManagerProxyLock = new Object();
+
private final Context mContext;
private final CarUserManagerHelper mCarUserManagerHelper;
private int mTaskCount = 0;
@@ -76,6 +83,69 @@
private Handler mHandler;
private CarPowerManager mCarPowerManager;
private CarDrivingStateService mCarDrivingStateService;
+ private PerUserCarServiceHelper mPerUserCarServiceHelper;
+
+ // Allows us to interact with the {@link LocationManager} as the foreground user.
+ private ILocationManagerProxy mILocationManagerProxy;
+
+ // Maintains mILocationManagerProxy for the current foreground user.
+ private final PerUserCarServiceHelper.ServiceCallback mUserServiceCallback =
+ new PerUserCarServiceHelper.ServiceCallback() {
+ @Override
+ public void onServiceConnected(ICarUserService carUserService) {
+ logd("Connected to PerUserCarService");
+ if (carUserService == null) {
+ logd("ICarUserService is null. Cannot get location manager proxy");
+ return;
+ }
+ synchronized (mLocationManagerProxyLock) {
+ try {
+ mILocationManagerProxy = carUserService.getLocationManagerProxy();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException from ICarUserService", e);
+ return;
+ }
+ }
+ int currentUser = ActivityManager.getCurrentUser();
+ logd("Current user: " + currentUser);
+ if (mCarUserManagerHelper.isHeadlessSystemUser()
+ && currentUser > UserHandle.USER_SYSTEM) {
+ asyncOperation(() -> loadLocation());
+ }
+ }
+
+ @Override
+ public void onPreUnbind() {
+ logd("Before Unbinding from PerCarUserService");
+ synchronized (mLocationManagerProxyLock) {
+ mILocationManagerProxy = null;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected() {
+ logd("Disconnected from PerUserCarService");
+ synchronized (mLocationManagerProxyLock) {
+ mILocationManagerProxy = null;
+ }
+ }
+ };
+
+ private final ICarDrivingStateChangeListener mICarDrivingStateChangeEventListener =
+ new ICarDrivingStateChangeListener.Stub() {
+ @Override
+ public void onDrivingStateChanged(CarDrivingStateEvent event) {
+ logd("onDrivingStateChanged " + event);
+ if (event != null
+ && event.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING) {
+ deleteCacheFile();
+ if (mCarDrivingStateService != null) {
+ mCarDrivingStateService.unregisterDrivingStateChangeListener(
+ mICarDrivingStateChangeEventListener);
+ }
+ }
+ }
+ };
public CarLocationService(Context context, CarUserManagerHelper carUserManagerHelper) {
logd("constructed");
@@ -87,9 +157,7 @@
public void init() {
logd("init");
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(LocationManager.MODE_CHANGED_ACTION);
- filter.addAction(LocationManager.PROVIDERS_CHANGED_ACTION);
mContext.registerReceiver(this, filter);
mCarDrivingStateService = CarLocalServices.getService(CarDrivingStateService.class);
if (mCarDrivingStateService != null) {
@@ -105,6 +173,10 @@
if (mCarPowerManager != null) { // null case happens for testing.
mCarPowerManager.setListenerWithCompletion(CarLocationService.this);
}
+ mPerUserCarServiceHelper = CarLocalServices.getService(PerUserCarServiceHelper.class);
+ if (mPerUserCarServiceHelper != null) {
+ mPerUserCarServiceHelper.registerServiceCallback(mUserServiceCallback);
+ }
}
@Override
@@ -117,6 +189,9 @@
mCarDrivingStateService.unregisterDrivingStateChangeListener(
mICarDrivingStateChangeEventListener);
}
+ if (mPerUserCarServiceHelper != null) {
+ mPerUserCarServiceHelper.unregisterServiceCallback(mUserServiceCallback);
+ }
mContext.unregisterReceiver(this);
}
@@ -158,70 +233,57 @@
@Override
public void onReceive(Context context, Intent intent) {
logd("onReceive " + intent);
- String action = intent.getAction();
- if (action == Intent.ACTION_USER_SWITCHED) {
- int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- logd("USER_SWITCHED: " + userHandle);
- if (mCarUserManagerHelper.isHeadlessSystemUser()
- && userHandle > UserHandle.USER_SYSTEM) {
- asyncOperation(() -> loadLocation());
+ // If the system user is headless but the current user is still the system user, then we
+ // should not delete the location cache file due to missing location permissions.
+ if (isCurrentUserHeadlessSystemUser()) {
+ logd("Current user is headless system user.");
+ return;
+ }
+ synchronized (mLocationManagerProxyLock) {
+ if (mILocationManagerProxy == null) {
+ logd("Null location manager.");
+ return;
}
- } else if (action == LocationManager.MODE_CHANGED_ACTION
- && shouldCheckLocationPermissions()) {
- LocationManager locationManager =
- (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
- boolean locationEnabled = locationManager.isLocationEnabled();
- logd("isLocationEnabled(): " + locationEnabled);
- if (!locationEnabled) {
- deleteCacheFile();
- }
- } else if (action == LocationManager.PROVIDERS_CHANGED_ACTION
- && shouldCheckLocationPermissions()) {
- LocationManager locationManager =
- (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
- boolean gpsEnabled =
- locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
- logd("isProviderEnabled('gps'): " + gpsEnabled);
- if (!gpsEnabled) {
- deleteCacheFile();
+ String action = intent.getAction();
+ try {
+ if (action == LocationManager.MODE_CHANGED_ACTION) {
+ boolean locationEnabled = mILocationManagerProxy.isLocationEnabled();
+ logd("isLocationEnabled(): " + locationEnabled);
+ if (!locationEnabled) {
+ deleteCacheFile();
+ }
+ } else {
+ logd("Unexpected intent.");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException from ILocationManagerProxy", e);
}
}
}
- private final ICarDrivingStateChangeListener mICarDrivingStateChangeEventListener =
- new ICarDrivingStateChangeListener.Stub() {
- @Override
- public void onDrivingStateChanged(CarDrivingStateEvent event) {
- logd("onDrivingStateChanged " + event);
- if (event != null
- && event.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING) {
- deleteCacheFile();
- if (mCarDrivingStateService != null) {
- mCarDrivingStateService.unregisterDrivingStateChangeListener(
- mICarDrivingStateChangeEventListener);
- }
- }
- }
- };
-
- /**
- * Tells whether or not we should check location permissions for the sake of deleting the
- * location cache file when permissions are lacking. If the system user is headless but the
- * current user is still the system user, then we should not respond to a lack of location
- * permissions.
- */
- private boolean shouldCheckLocationPermissions() {
- return !(mCarUserManagerHelper.isHeadlessSystemUser()
- && mCarUserManagerHelper.isCurrentProcessSystemUser());
+ /** Tells whether the current foreground user is the headless system user. */
+ private boolean isCurrentUserHeadlessSystemUser() {
+ int currentUserId = ActivityManager.getCurrentUser();
+ return mCarUserManagerHelper.isHeadlessSystemUser() && currentUserId == 0;
}
/**
- * Gets the last known location from the LocationManager and store it in a file.
+ * Gets the last known location from the location manager proxy and store it in a file.
*/
private void storeLocation() {
- LocationManager locationManager =
- (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
- Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+ Location location = null;
+ synchronized (mLocationManagerProxyLock) {
+ if (mILocationManagerProxy == null) {
+ logd("Null location manager proxy.");
+ return;
+ }
+ try {
+ location = mILocationManagerProxy.getLastKnownLocation(
+ LocationManager.GPS_PROVIDER);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException from ILocationManagerProxy", e);
+ }
+ }
if (location == null) {
logd("Not storing null location");
} else {
@@ -277,7 +339,7 @@
}
/**
- * Reads a previously stored location and attempts to inject it into the LocationManager.
+ * Reads a previously stored location and attempts to inject it into the location manager proxy.
*/
private void loadLocation() {
Location location = readLocationFromCacheFile();
@@ -353,9 +415,18 @@
* initialized or has not updated its handle to the current user yet.
*/
private void injectLocation(Location location, int attemptCount) {
- LocationManager locationManager =
- (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
- boolean success = locationManager.injectLocation(location);
+ boolean success = false;
+ synchronized (mLocationManagerProxyLock) {
+ if (mILocationManagerProxy == null) {
+ logd("Null location manager proxy.");
+ } else {
+ try {
+ success = mILocationManagerProxy.injectLocation(location);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException from ILocationManagerProxy", e);
+ }
+ }
+ }
logd("Injected location " + location + " with result " + success + " on attempt "
+ attemptCount);
if (success) {
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 27bc0ae..0335186 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -172,6 +172,7 @@
CarLocalServices.addService(CarTrustedDeviceService.class, mCarTrustedDeviceService);
CarLocalServices.addService(SystemInterface.class, mSystemInterface);
CarLocalServices.addService(CarDrivingStateService.class, mCarDrivingStateService);
+ CarLocalServices.addService(PerUserCarServiceHelper.class, mPerUserCarServiceHelper);
// Be careful with order. Service depending on other service should be inited later.
List<CarServiceBase> allServices = new ArrayList<>();
diff --git a/service/src/com/android/car/LocationManagerProxy.java b/service/src/com/android/car/LocationManagerProxy.java
new file mode 100644
index 0000000..8ace92f
--- /dev/null
+++ b/service/src/com/android/car/LocationManagerProxy.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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.car;
+
+import android.annotation.NonNull;
+import android.car.ILocationManagerProxy;
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationManager;
+import android.util.Log;
+
+/** Wraps a {@link LocationManager}. */
+public class LocationManagerProxy extends ILocationManagerProxy.Stub {
+ private static final String TAG = "LocationManagerProxy";
+ private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final LocationManager mLocationManager;
+
+ /**
+ * Create a LocationManagerProxy instance given a {@link Context}.
+ */
+ public LocationManagerProxy(Context context) {
+ if (DBG) {
+ Log.d(TAG, "constructed.");
+ }
+ mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+ }
+
+ @Override
+ public boolean isLocationEnabled() {
+ return mLocationManager.isLocationEnabled();
+ }
+
+ @Override
+ public boolean injectLocation(Location location) {
+ return mLocationManager.injectLocation(location);
+ }
+
+ @Override
+ public Location getLastKnownLocation(@NonNull String provider) {
+ if (DBG) {
+ Log.d(TAG, "Getting last known location for provider " + provider);
+ }
+ return mLocationManager.getLastKnownLocation(provider);
+ }
+}
diff --git a/service/src/com/android/car/PerUserCarService.java b/service/src/com/android/car/PerUserCarService.java
index 406456b..dec2b3a 100644
--- a/service/src/com/android/car/PerUserCarService.java
+++ b/service/src/com/android/car/PerUserCarService.java
@@ -18,6 +18,7 @@
import android.app.Service;
import android.car.ICarBluetoothUserService;
import android.car.ICarUserService;
+import android.car.ILocationManagerProxy;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
@@ -33,7 +34,8 @@
public class PerUserCarService extends Service {
private static final boolean DBG = true;
private static final String TAG = "CarUserService";
- private CarBluetoothUserService mCarBluetoothUserService;
+ private volatile CarBluetoothUserService mCarBluetoothUserService;
+ private volatile LocationManagerProxy mLocationManagerProxy;
private CarUserServiceBinder mCarUserServiceBinder;
@Override
@@ -60,7 +62,9 @@
if (DBG) {
Log.d(TAG, "onCreate()");
}
- mCarUserServiceBinder = new CarUserServiceBinder(this);
+ mCarUserServiceBinder = new CarUserServiceBinder();
+ mCarBluetoothUserService = new CarBluetoothUserService(this);
+ mLocationManagerProxy = new LocationManagerProxy(this);
super.onCreate();
}
@@ -69,35 +73,22 @@
if (DBG) {
Log.d(TAG, "onDestroy()");
}
- mCarBluetoothUserService = null;
mCarUserServiceBinder = null;
}
- public void createBluetoothUserService() {
- if (DBG) {
- Log.d(TAG, "createBluetoothUserService");
- }
- mCarBluetoothUserService = new CarBluetoothUserService(this);
- }
-
/**
* Other Services in CarService can create their own Binder interface and receive that interface
* through this CarUserService binder.
*/
private final class CarUserServiceBinder extends ICarUserService.Stub {
- private PerUserCarService mCarUserService;
-
- public CarUserServiceBinder(PerUserCarService service) {
- mCarUserService = service;
+ @Override
+ public ICarBluetoothUserService getBluetoothUserService() {
+ return mCarBluetoothUserService;
}
@Override
- public ICarBluetoothUserService getBluetoothUserService() {
- // Create the bluetoothUserService when needed.
- if (mCarBluetoothUserService == null) {
- mCarUserService.createBluetoothUserService();
- }
- return mCarBluetoothUserService;
+ public ILocationManagerProxy getLocationManagerProxy() {
+ return mLocationManagerProxy;
}
}
}
diff --git a/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java
index d08d32a..c9e85e0 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java
@@ -16,6 +16,8 @@
package com.android.car;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -23,11 +25,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.car.ICarUserService;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.ICarDrivingStateChangeListener;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
@@ -45,7 +49,6 @@
import com.android.car.systeminterface.SystemInterface;
import com.android.car.test.utils.TemporaryDirectory;
-import com.android.internal.util.ArrayUtils;
import org.junit.After;
import org.junit.Before;
@@ -67,15 +70,17 @@
/**
* This class contains unit tests for the {@link CarLocationService}.
- * It tests that {@link LocationManager}'s last known location is stored in and loaded from a JSON
- * file upon appropriate system events.
+ * It tests that {@link LocationManagerProxy}'s last known location is stored in and loaded from a
+ * JSON file upon appropriate system events.
*
* The following mocks are used:
- * 1. {@link Context} provides a mocked {@link LocationManager}.
- * 2. {@link LocationManager} provides dummy {@link Location}s.
- * 3. {@link CarUserManagerHelper} tells whether or not the system user is headless.
- * 4. {@link SystemInterface} tells where to store system files.
- * 5. {@link CarDrivingStateService} tells about driving state changes.
+ * 1. {@link Context} registers intent receivers.
+ * 2. {@link CarUserManagerHelper} tells whether or not the system user is headless.
+ * 3. {@link SystemInterface} tells where to store system files.
+ * 4. {@link CarDrivingStateService} tells about driving state changes.
+ * 5. {@link PerUserCarServiceHelper} provides a mocked {@link ICarUserService}.
+ * 6. {@link ICarUserService} provides a mocked {@link LocationManagerProxy}.
+ * 7. {@link LocationManagerProxy} provides dummy {@link Location}s.
*/
@RunWith(AndroidJUnit4.class)
public class CarLocationServiceTest {
@@ -85,16 +90,21 @@
private Context mContext;
private CountDownLatch mLatch;
private File mTempDirectory;
+ private PerUserCarServiceHelper.ServiceCallback mUserServiceCallback;
@Mock
private Context mMockContext;
@Mock
- private LocationManager mMockLocationManager;
+ private LocationManagerProxy mMockLocationManagerProxy;
@Mock
private CarUserManagerHelper mMockCarUserManagerHelper;
@Mock
private SystemInterface mMockSystemInterface;
@Mock
private CarDrivingStateService mMockCarDrivingStateService;
+ @Mock
+ private PerUserCarServiceHelper mMockPerUserCarServiceHelper;
+ @Mock
+ private ICarUserService mMockICarUserService;
/**
* Initialize all of the objects with the @Mock annotation.
@@ -118,7 +128,24 @@
CarLocalServices.addService(SystemInterface.class, mMockSystemInterface);
CarLocalServices.removeServiceForTest(CarDrivingStateService.class);
CarLocalServices.addService(CarDrivingStateService.class, mMockCarDrivingStateService);
+ CarLocalServices.removeServiceForTest(PerUserCarServiceHelper.class);
+ CarLocalServices.addService(PerUserCarServiceHelper.class, mMockPerUserCarServiceHelper);
when(mMockSystemInterface.getSystemCarDir()).thenReturn(mTempDirectory);
+ when(mMockICarUserService.getLocationManagerProxy()).thenReturn(mMockLocationManagerProxy);
+
+ // We only support and test the headless system user case.
+ when(mMockCarUserManagerHelper.isHeadlessSystemUser()).thenReturn(true);
+
+ // Store CarLocationService's user switch callback so we can invoke it in the tests.
+ doAnswer((invocation) -> {
+ Object[] arguments = invocation.getArguments();
+ assertThat(arguments).isNotNull();
+ assertThat(arguments).hasLength(1);
+ assertThat(arguments[0]).isNotNull();
+ mUserServiceCallback = (PerUserCarServiceHelper.ServiceCallback) arguments[0];
+ return null;
+ }).when(mMockPerUserCarServiceHelper).registerServiceCallback(any(
+ PerUserCarServiceHelper.ServiceCallback.class));
}
@After
@@ -146,23 +173,20 @@
}
/**
- * Test that the {@link CarLocationService} registers to receive location and user intents.
+ * Test that the {@link CarLocationService} registers to receive location intents.
*/
@Test
public void testRegistersToReceiveEvents() {
+ mCarLocationService.init();
ArgumentCaptor<IntentFilter> intentFilterArgument = ArgumentCaptor.forClass(
IntentFilter.class);
- mCarLocationService.init();
verify(mMockContext).registerReceiver(eq(mCarLocationService),
intentFilterArgument.capture());
verify(mMockCarDrivingStateService).registerDrivingStateChangeListener(any());
+ verify(mMockPerUserCarServiceHelper).registerServiceCallback(any());
IntentFilter intentFilter = intentFilterArgument.getValue();
- assertEquals(3, intentFilter.countActions());
- String[] actions = {intentFilter.getAction(0), intentFilter.getAction(1),
- intentFilter.getAction(2)};
- assertTrue(ArrayUtils.contains(actions, LocationManager.MODE_CHANGED_ACTION));
- assertTrue(ArrayUtils.contains(actions, LocationManager.PROVIDERS_CHANGED_ACTION));
- assertTrue(ArrayUtils.contains(actions, Intent.ACTION_USER_SWITCHED));
+ assertThat(intentFilter.countActions()).isEqualTo(1);
+ assertThat(intentFilter.getAction(0)).isEqualTo(LocationManager.MODE_CHANGED_ACTION);
}
/**
@@ -174,28 +198,26 @@
mCarLocationService.release();
verify(mMockContext).unregisterReceiver(mCarLocationService);
verify(mMockCarDrivingStateService).unregisterDrivingStateChangeListener(any());
+ verify(mMockPerUserCarServiceHelper).unregisterServiceCallback(any());
}
/**
* Test that the {@link CarLocationService} parses a location from a JSON serialization and then
- * injects it into the {@link LocationManager} upon user switch.
+ * injects it into the {@link LocationManagerProxy} upon onServiceConnected.
*/
@Test
public void testLoadsLocationWithHeadlessSystemUser() throws Exception {
+ mCarLocationService.init();
+ assertThat(mUserServiceCallback).isNotNull();
long currentTime = System.currentTimeMillis();
long elapsedTime = SystemClock.elapsedRealtimeNanos();
long pastTime = currentTime - 60000;
writeCacheFile("{\"provider\": \"gps\", \"latitude\": 16.7666, \"longitude\": 3.0026,"
+ "\"accuracy\":12.3, \"captureTime\": " + pastTime + "}");
ArgumentCaptor<Location> argument = ArgumentCaptor.forClass(Location.class);
- when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mMockLocationManager);
- when(mMockLocationManager.injectLocation(argument.capture())).thenReturn(true);
- when(mMockCarUserManagerHelper.isHeadlessSystemUser()).thenReturn(true);
+ when(mMockLocationManagerProxy.injectLocation(argument.capture())).thenReturn(true);
- Intent userSwitchedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
- userSwitchedIntent.putExtra(Intent.EXTRA_USER_HANDLE, 11);
- mCarLocationService.onReceive(mMockContext, userSwitchedIntent);
+ mUserServiceCallback.onServiceConnected(mMockICarUserService);
mLatch.await();
Location location = argument.getValue();
@@ -213,16 +235,14 @@
*/
@Test
public void testDoesNotLoadLocationWhenNoFileExists() throws Exception {
- when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mMockLocationManager);
- when(mMockLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER))
- .thenReturn(null);
- when(mMockCarUserManagerHelper.isHeadlessSystemUser()).thenReturn(true);
- Intent userSwitchedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
- userSwitchedIntent.putExtra(Intent.EXTRA_USER_HANDLE, 11);
- mCarLocationService.onReceive(mMockContext, userSwitchedIntent);
+ mCarLocationService.init();
+ assertThat(mUserServiceCallback).isNotNull();
+ assertThat(getLocationCacheFile().exists()).isFalse();
+
+ mUserServiceCallback.onServiceConnected(mMockICarUserService);
mLatch.await();
- verify(mMockLocationManager, never()).injectLocation(any());
+
+ verify(mMockLocationManagerProxy, never()).injectLocation(any());
}
/**
@@ -230,17 +250,14 @@
*/
@Test
public void testDoesNotLoadLocationFromIncompleteFile() throws Exception {
+ mCarLocationService.init();
+ assertThat(mUserServiceCallback).isNotNull();
writeCacheFile("{\"provider\": \"gps\", \"latitude\": 16.7666, \"longitude\": 3.0026,");
- when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mMockLocationManager);
- when(mMockLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER))
- .thenReturn(null);
- when(mMockCarUserManagerHelper.isHeadlessSystemUser()).thenReturn(true);
- Intent userSwitchedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
- userSwitchedIntent.putExtra(Intent.EXTRA_USER_HANDLE, 11);
- mCarLocationService.onReceive(mMockContext, userSwitchedIntent);
+
+ mUserServiceCallback.onServiceConnected(mMockICarUserService);
mLatch.await();
- verify(mMockLocationManager, never()).injectLocation(any());
+
+ verify(mMockLocationManagerProxy, never()).injectLocation(any());
}
/**
@@ -248,17 +265,14 @@
*/
@Test
public void testDoesNotLoadLocationFromCorruptFile() throws Exception {
+ mCarLocationService.init();
+ assertThat(mUserServiceCallback).isNotNull();
writeCacheFile("{\"provider\":\"latitude\":16.7666,\"longitude\": \"accuracy\":1.0}");
- when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mMockLocationManager);
- when(mMockLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER))
- .thenReturn(null);
- when(mMockCarUserManagerHelper.isHeadlessSystemUser()).thenReturn(true);
- Intent userSwitchedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
- userSwitchedIntent.putExtra(Intent.EXTRA_USER_HANDLE, 11);
- mCarLocationService.onReceive(mMockContext, userSwitchedIntent);
+
+ mUserServiceCallback.onServiceConnected(mMockICarUserService);
mLatch.await();
- verify(mMockLocationManager, never()).injectLocation(any());
+
+ verify(mMockLocationManagerProxy, never()).injectLocation(any());
}
/**
@@ -267,17 +281,14 @@
*/
@Test
public void testDoesNotLoadIncompleteLocation() throws Exception {
+ mCarLocationService.init();
+ assertThat(mUserServiceCallback).isNotNull();
writeCacheFile("{\"provider\": \"gps\", \"latitude\": 16.7666, \"longitude\": 3.0026}");
- when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mMockLocationManager);
- when(mMockLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER))
- .thenReturn(null);
- when(mMockCarUserManagerHelper.isHeadlessSystemUser()).thenReturn(true);
- Intent userSwitchedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
- userSwitchedIntent.putExtra(Intent.EXTRA_USER_HANDLE, 11);
- mCarLocationService.onReceive(mMockContext, userSwitchedIntent);
+
+ mUserServiceCallback.onServiceConnected(mMockICarUserService);
mLatch.await();
- verify(mMockLocationManager, never()).injectLocation(any());
+
+ verify(mMockLocationManagerProxy, never()).injectLocation(any());
}
/**
@@ -286,20 +297,17 @@
*/
@Test
public void testDoesNotLoadOldLocation() throws Exception {
+ mCarLocationService.init();
+ assertThat(mUserServiceCallback).isNotNull();
long thirtyThreeDaysMs = 33 * 24 * 60 * 60 * 1000L;
long oldTime = System.currentTimeMillis() - thirtyThreeDaysMs;
writeCacheFile("{\"provider\": \"gps\", \"latitude\": 16.7666, \"longitude\": 3.0026,"
+ "\"accuracy\":12.3, \"captureTime\": " + oldTime + "}");
- when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mMockLocationManager);
- when(mMockLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER))
- .thenReturn(null);
- when(mMockCarUserManagerHelper.isHeadlessSystemUser()).thenReturn(true);
- Intent userSwitchedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
- userSwitchedIntent.putExtra(Intent.EXTRA_USER_HANDLE, 11);
- mCarLocationService.onReceive(mMockContext, userSwitchedIntent);
+
+ mUserServiceCallback.onServiceConnected(mMockICarUserService);
mLatch.await();
- verify(mMockLocationManager, never()).injectLocation(any());
+
+ verify(mMockLocationManagerProxy, never()).injectLocation(any());
}
/**
@@ -308,6 +316,13 @@
*/
@Test
public void testStoresLocationUponShutdownPrepare() throws Exception {
+ // We must have a LocationManagerProxy for the current user in order to get a location
+ // during shutdown-prepare.
+ mCarLocationService.init();
+ mUserServiceCallback.onServiceConnected(mMockICarUserService);
+ mLatch.await();
+ mLatch = new CountDownLatch(1);
+
long currentTime = System.currentTimeMillis();
long elapsedTime = SystemClock.elapsedRealtimeNanos();
Location timbuktu = new Location(LocationManager.GPS_PROVIDER);
@@ -316,14 +331,14 @@
timbuktu.setAccuracy(13.75f);
timbuktu.setTime(currentTime);
timbuktu.setElapsedRealtimeNanos(elapsedTime);
- when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mMockLocationManager);
- when(mMockLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER))
+ when(mMockLocationManagerProxy.getLastKnownLocation(LocationManager.GPS_PROVIDER))
.thenReturn(timbuktu);
CompletableFuture<Void> future = new CompletableFuture<>();
+
mCarLocationService.onStateChanged(CarPowerStateListener.SHUTDOWN_PREPARE, future);
mLatch.await();
- verify(mMockLocationManager).getLastKnownLocation(LocationManager.GPS_PROVIDER);
+
+ verify(mMockLocationManagerProxy).getLastKnownLocation(LocationManager.GPS_PROVIDER);
assertTrue(future.isDone());
String actualContents = readCacheFile();
long oneDayMs = 24 * 60 * 60 * 1000;
@@ -355,14 +370,22 @@
*/
@Test
public void testDoesNotStoreNullLocation() throws Exception {
- when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mMockLocationManager);
- when(mMockLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER))
+ // We must have a LocationManagerProxy for the current user in order to get a location
+ // during shutdown-prepare.
+ mCarLocationService.init();
+ mUserServiceCallback.onServiceConnected(mMockICarUserService);
+ mLatch.await();
+ mLatch = new CountDownLatch(1);
+
+ when(mMockLocationManagerProxy.getLastKnownLocation(LocationManager.GPS_PROVIDER))
.thenReturn(null);
CompletableFuture<Void> future = new CompletableFuture<>();
+
mCarLocationService.onStateChanged(CarPowerStateListener.SHUTDOWN_PREPARE, future);
mLatch.await();
- verify(mMockLocationManager).getLastKnownLocation(LocationManager.GPS_PROVIDER);
+
+ assertTrue(future.isDone());
+ verify(mMockLocationManagerProxy).getLastKnownLocation(LocationManager.GPS_PROVIDER);
assertFalse(getLocationCacheFile().exists());
}
@@ -372,17 +395,21 @@
*/
@Test
public void testDeletesCacheFileWhenLocationIsDisabled() throws Exception {
- writeCacheFile("{\"provider\":\"latitude\":16.7666,\"longitude\": \"accuracy\":1.0}");
- when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mMockLocationManager);
- when(mMockLocationManager.isLocationEnabled()).thenReturn(false);
+ // We must have a LocationManagerProxy for the current user in order to check whether or
+ // not location is enabled.
mCarLocationService.init();
+ mUserServiceCallback.onServiceConnected(mMockICarUserService);
+ mLatch.await();
+ mLatch = new CountDownLatch(1);
+
+ writeCacheFile("{\"provider\":\"latitude\":16.7666,\"longitude\": \"accuracy\":1.0}");
+ when(mMockLocationManagerProxy.isLocationEnabled()).thenReturn(false);
assertTrue(getLocationCacheFile().exists());
mCarLocationService.onReceive(mMockContext,
new Intent(LocationManager.MODE_CHANGED_ACTION));
- verify(mMockLocationManager, times(1)).isLocationEnabled();
+ verify(mMockLocationManagerProxy, times(1)).isLocationEnabled();
assertFalse(getLocationCacheFile().exists());
}
@@ -392,36 +419,14 @@
*/
@Test
public void testDeletesCacheFileUponSuspendExit() throws Exception {
- when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mMockLocationManager);
- when(mMockLocationManager.isLocationEnabled()).thenReturn(false);
mCarLocationService.init();
+ when(mMockLocationManagerProxy.isLocationEnabled()).thenReturn(false);
CompletableFuture<Void> future = new CompletableFuture<>();
+
mCarLocationService.onStateChanged(CarPowerStateListener.SUSPEND_EXIT, future);
+
assertTrue(future.isDone());
- verify(mMockLocationManager, times(0)).isLocationEnabled();
- assertFalse(getLocationCacheFile().exists());
- }
-
- /**
- * Test that the {@link CarLocationService} deletes location_cache.json when the GPS location
- * provider is disabled.
- */
- @Test
- public void testDeletesCacheFileWhenTheGPSProviderIsDisabled() throws Exception {
- writeCacheFile("{\"provider\":\"latitude\":16.7666,\"longitude\": \"accuracy\":1.0}");
- when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mMockLocationManager);
- when(mMockLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)).thenReturn(
- false);
- mCarLocationService.init();
- assertTrue(getLocationCacheFile().exists());
-
- mCarLocationService.onReceive(mMockContext,
- new Intent(LocationManager.PROVIDERS_CHANGED_ACTION));
-
- verify(mMockLocationManager, times(1))
- .isProviderEnabled(LocationManager.GPS_PROVIDER);
+ verify(mMockLocationManagerProxy, times(0)).isLocationEnabled();
assertFalse(getLocationCacheFile().exists());
}
@@ -431,11 +436,9 @@
*/
@Test
public void testDeletesCacheFileWhenDrivingStateBecomesMoving() throws Exception {
- writeCacheFile("{\"provider\":\"latitude\":16.7666,\"longitude\": \"accuracy\":1.0}");
- when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mMockLocationManager);
- when(mMockLocationManager.isLocationEnabled()).thenReturn(false);
mCarLocationService.init();
+ writeCacheFile("{\"provider\":\"latitude\":16.7666,\"longitude\": \"accuracy\":1.0}");
+ when(mMockLocationManagerProxy.isLocationEnabled()).thenReturn(false);
ArgumentCaptor<ICarDrivingStateChangeListener> changeListenerArgument =
ArgumentCaptor.forClass(ICarDrivingStateChangeListener.class);
verify(mMockCarDrivingStateService).registerDrivingStateChangeListener(
@@ -447,7 +450,7 @@
new CarDrivingStateEvent(CarDrivingStateEvent.DRIVING_STATE_MOVING,
SystemClock.elapsedRealtimeNanos()));
- verify(mMockLocationManager, times(0)).isLocationEnabled();
+ verify(mMockLocationManagerProxy, times(0)).isLocationEnabled();
verify(mMockCarDrivingStateService, times(1)).unregisterDrivingStateChangeListener(any());
assertFalse(getLocationCacheFile().exists());
}
diff --git a/tests/carservice_unit_test/src/com/android/car/LocationManagerProxyTest.java b/tests/carservice_unit_test/src/com/android/car/LocationManagerProxyTest.java
new file mode 100644
index 0000000..6fa4e3b
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/LocationManagerProxyTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 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.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * This class contains unit tests for {@link LocationManagerProxy}.
+ *
+ * Mocks are used for {@link Context} and {@link LocationManager}.
+ */
+public class LocationManagerProxyTest {
+ private static final String TAG = "LocationManagerProxyTest";
+ private LocationManagerProxy mLocationManagerProxy;
+ @Mock
+ private Context mMockContext;
+ @Mock
+ private LocationManager mMockLocationManager;
+
+ /** Initialize all of the objects with the @Mock annotation. */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
+ .thenReturn(mMockLocationManager);
+ mLocationManagerProxy = new LocationManagerProxy(mMockContext);
+ }
+
+ @Test
+ public void testLocationManagerProxyCanInjectLocation() {
+ Location location = new Location(LocationManager.GPS_PROVIDER);
+ mLocationManagerProxy.injectLocation(location);
+ verify(mMockLocationManager).injectLocation(location);
+ }
+
+ @Test
+ public void testLocationManagerProxyCanGetLastKnownLocation() {
+ Location location = new Location(LocationManager.GPS_PROVIDER);
+ when(mMockLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER))
+ .thenReturn(location);
+ Location locationFromProxy = mLocationManagerProxy
+ .getLastKnownLocation(LocationManager.GPS_PROVIDER);
+ assertThat(locationFromProxy).isEqualTo(location);
+ }
+
+ @Test
+ public void testLocationManagerProxyCanCheckIfLocationIsEnabled() {
+ when(mMockLocationManager.isLocationEnabled()).thenReturn(true);
+ assertThat(mLocationManagerProxy.isLocationEnabled()).isTrue();
+
+ when(mMockLocationManager.isLocationEnabled()).thenReturn(false);
+ assertThat(mLocationManagerProxy.isLocationEnabled()).isFalse();
+ }
+}