Merge "After using a cached location, keep it until the car starts moving." into qt-dev
diff --git a/service/src/com/android/car/CarLocationService.java b/service/src/com/android/car/CarLocationService.java
index 43d4a46..cc57856 100644
--- a/service/src/com/android/car/CarLocationService.java
+++ b/service/src/com/android/car/CarLocationService.java
@@ -16,6 +16,8 @@
package com.android.car;
+import android.car.drivingstate.CarDrivingStateEvent;
+import android.car.drivingstate.ICarDrivingStateChangeListener;
import android.car.hardware.power.CarPowerManager;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
import android.car.hardware.power.CarPowerManager.CarPowerStateListenerWithCompletion;
@@ -52,8 +54,8 @@
* This service stores the last known location from {@link LocationManager} when a car is parked
* and restores the location when the car is powered on.
*/
-public class CarLocationService extends BroadcastReceiver implements
- CarServiceBase, CarPowerStateListenerWithCompletion {
+public class CarLocationService extends BroadcastReceiver implements CarServiceBase,
+ CarPowerStateListenerWithCompletion {
private static final String TAG = "CarLocationService";
private static final String FILENAME = "location_cache.json";
private static final boolean DBG = true;
@@ -73,10 +75,9 @@
private HandlerThread mHandlerThread;
private Handler mHandler;
private CarPowerManager mCarPowerManager;
+ private CarDrivingStateService mCarDrivingStateService;
- public CarLocationService(
- Context context,
- CarUserManagerHelper carUserManagerHelper) {
+ public CarLocationService(Context context, CarUserManagerHelper carUserManagerHelper) {
logd("constructed");
mContext = context;
mCarUserManagerHelper = carUserManagerHelper;
@@ -90,6 +91,16 @@
filter.addAction(LocationManager.MODE_CHANGED_ACTION);
filter.addAction(LocationManager.PROVIDERS_CHANGED_ACTION);
mContext.registerReceiver(this, filter);
+ mCarDrivingStateService = CarLocalServices.getService(CarDrivingStateService.class);
+ if (mCarDrivingStateService != null) {
+ CarDrivingStateEvent event = mCarDrivingStateService.getCurrentDrivingState();
+ if (event != null && event.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING) {
+ deleteCacheFile();
+ } else {
+ mCarDrivingStateService.registerDrivingStateChangeListener(
+ mICarDrivingStateChangeEventListener);
+ }
+ }
mCarPowerManager = CarLocalServices.createCarPowerManager(mContext);
if (mCarPowerManager != null) { // null case happens for testing.
mCarPowerManager.setListenerWithCompletion(CarLocationService.this);
@@ -102,6 +113,10 @@
if (mCarPowerManager != null) {
mCarPowerManager.clearListener();
}
+ if (mCarDrivingStateService != null) {
+ mCarDrivingStateService.unregisterDrivingStateChangeListener(
+ mICarDrivingStateChangeEventListener);
+ }
mContext.unregisterReceiver(this);
}
@@ -173,6 +188,22 @@
}
}
+ 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
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 244ee8b..502ebbb 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -169,9 +169,9 @@
CarLocalServices.addService(CarPowerManagementService.class, mCarPowerManagementService);
CarLocalServices.addService(CarUserService.class, mCarUserService);
- CarLocalServices.addService(CarTrustedDeviceService.class,
- mCarTrustedDeviceService);
+ CarLocalServices.addService(CarTrustedDeviceService.class, mCarTrustedDeviceService);
CarLocalServices.addService(SystemInterface.class, mSystemInterface);
+ CarLocalServices.addService(CarDrivingStateService.class, mCarDrivingStateService);
// Be careful with order. Service depending on other service should be inited later.
List<CarServiceBase> allServices = new ArrayList<>();
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 4290354..d08d32a 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java
@@ -28,6 +28,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.car.drivingstate.CarDrivingStateEvent;
+import android.car.drivingstate.ICarDrivingStateChangeListener;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
import android.car.userlib.CarUserManagerHelper;
import android.content.Context;
@@ -73,6 +75,7 @@
* 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.
*/
@RunWith(AndroidJUnit4.class)
public class CarLocationServiceTest {
@@ -90,6 +93,8 @@
private CarUserManagerHelper mMockCarUserManagerHelper;
@Mock
private SystemInterface mMockSystemInterface;
+ @Mock
+ private CarDrivingStateService mMockCarDrivingStateService;
/**
* Initialize all of the objects with the @Mock annotation.
@@ -111,6 +116,9 @@
};
CarLocalServices.removeServiceForTest(SystemInterface.class);
CarLocalServices.addService(SystemInterface.class, mMockSystemInterface);
+ CarLocalServices.removeServiceForTest(CarDrivingStateService.class);
+ CarLocalServices.addService(CarDrivingStateService.class, mMockCarDrivingStateService);
+ when(mMockSystemInterface.getSystemCarDir()).thenReturn(mTempDirectory);
}
@After
@@ -142,10 +150,13 @@
*/
@Test
public void testRegistersToReceiveEvents() {
- ArgumentCaptor<IntentFilter> argument = ArgumentCaptor.forClass(IntentFilter.class);
+ ArgumentCaptor<IntentFilter> intentFilterArgument = ArgumentCaptor.forClass(
+ IntentFilter.class);
mCarLocationService.init();
- verify(mMockContext).registerReceiver(eq(mCarLocationService), argument.capture());
- IntentFilter intentFilter = argument.getValue();
+ verify(mMockContext).registerReceiver(eq(mCarLocationService),
+ intentFilterArgument.capture());
+ verify(mMockCarDrivingStateService).registerDrivingStateChangeListener(any());
+ IntentFilter intentFilter = intentFilterArgument.getValue();
assertEquals(3, intentFilter.countActions());
String[] actions = {intentFilter.getAction(0), intentFilter.getAction(1),
intentFilter.getAction(2)};
@@ -159,8 +170,10 @@
*/
@Test
public void testUnregistersEventReceivers() {
+ mCarLocationService.init();
mCarLocationService.release();
verify(mMockContext).unregisterReceiver(mCarLocationService);
+ verify(mMockCarDrivingStateService).unregisterDrivingStateChangeListener(any());
}
/**
@@ -177,7 +190,6 @@
ArgumentCaptor<Location> argument = ArgumentCaptor.forClass(Location.class);
when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
.thenReturn(mMockLocationManager);
- when(mMockSystemInterface.getSystemCarDir()).thenReturn(mTempDirectory);
when(mMockLocationManager.injectLocation(argument.capture())).thenReturn(true);
when(mMockCarUserManagerHelper.isHeadlessSystemUser()).thenReturn(true);
@@ -304,7 +316,6 @@
timbuktu.setAccuracy(13.75f);
timbuktu.setTime(currentTime);
timbuktu.setElapsedRealtimeNanos(elapsedTime);
- when(mMockSystemInterface.getSystemCarDir()).thenReturn(mTempDirectory);
when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
.thenReturn(mMockLocationManager);
when(mMockLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER))
@@ -326,7 +337,7 @@
* Test that the {@link CarLocationService} does not throw an exception on SUSPEND_EXIT events.
*/
@Test
- public void testDoesNotThrowExceptionUponStateChanged() {
+ public void testDoesNotThrowExceptionUponPowerStateChanged() {
try {
mCarLocationService.onStateChanged(CarPowerStateListener.SUSPEND_ENTER, null);
mCarLocationService.onStateChanged(CarPowerStateListener.SUSPEND_EXIT, null);
@@ -361,12 +372,16 @@
*/
@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);
mCarLocationService.init();
+ assertTrue(getLocationCacheFile().exists());
+
mCarLocationService.onReceive(mMockContext,
new Intent(LocationManager.MODE_CHANGED_ACTION));
+
verify(mMockLocationManager, times(1)).isLocationEnabled();
assertFalse(getLocationCacheFile().exists());
}
@@ -394,18 +409,49 @@
*/
@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);
assertFalse(getLocationCacheFile().exists());
}
+ /**
+ * Test that the {@link CarLocationService} deletes location_cache.json when the car enters a
+ * moving driving state.
+ */
+ @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();
+ ArgumentCaptor<ICarDrivingStateChangeListener> changeListenerArgument =
+ ArgumentCaptor.forClass(ICarDrivingStateChangeListener.class);
+ verify(mMockCarDrivingStateService).registerDrivingStateChangeListener(
+ changeListenerArgument.capture());
+ ICarDrivingStateChangeListener changeListener = changeListenerArgument.getValue();
+ assertTrue(getLocationCacheFile().exists());
+
+ changeListener.onDrivingStateChanged(
+ new CarDrivingStateEvent(CarDrivingStateEvent.DRIVING_STATE_MOVING,
+ SystemClock.elapsedRealtimeNanos()));
+
+ verify(mMockLocationManager, times(0)).isLocationEnabled();
+ verify(mMockCarDrivingStateService, times(1)).unregisterDrivingStateChangeListener(any());
+ assertFalse(getLocationCacheFile().exists());
+ }
+
private void writeCacheFile(String json) throws IOException {
FileOutputStream fos = new FileOutputStream(getLocationCacheFile());
fos.write(json.getBytes());