After using a cached location, keep it until the car starts moving.

Bug: b/129371464
Test: atest CarLocationServiceTest
Change-Id: I09f126a12ff2513d400eb75b9fb0189ecc496211
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());