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();
+    }
+}