diff options
author | Gregory Clark <gregoryclark@google.com> | 2018-06-07 16:42:12 -0700 |
---|---|---|
committer | Gregory Clark <gregoryclark@google.com> | 2018-06-19 18:19:16 -0700 |
commit | a63ba02a9279dc55cc3ef77dbec02c54383afdbe (patch) | |
tree | 0352b73e396f1a47b5eabd58cbd93decce1302ec /service/src/com/android | |
parent | cf6d16cbadc4916e67c60573178791cf25b3ff09 (diff) | |
download | Car-a63ba02a9279dc55cc3ef77dbec02c54383afdbe.tar.gz |
Update the CarLocationService to work when the system user is headless.
Bug: b/79772231
Test: Tested on Owl and ran atest CarLocationServiceTest
Change-Id: I1598f0fba0ba5335cb1d9c81e16d1db8d7922df0
Diffstat (limited to 'service/src/com/android')
-rw-r--r-- | service/src/com/android/car/CarLocationService.java | 188 | ||||
-rw-r--r-- | service/src/com/android/car/ICarImpl.java | 6 |
2 files changed, 127 insertions, 67 deletions
diff --git a/service/src/com/android/car/CarLocationService.java b/service/src/com/android/car/CarLocationService.java index 6d93b40c7e..a7aaf03503 100644 --- a/service/src/com/android/car/CarLocationService.java +++ b/service/src/com/android/car/CarLocationService.java @@ -19,6 +19,7 @@ package com.android.car; import android.car.hardware.CarPropertyValue; import android.car.hardware.property.CarPropertyEvent; import android.car.hardware.property.ICarPropertyEventListener; +import android.car.user.CarUserManagerHelper; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -31,6 +32,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.RemoteException; import android.os.SystemClock; +import android.os.UserHandle; import android.util.AtomicFile; import android.util.JsonReader; import android.util.JsonWriter; @@ -60,6 +62,8 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB private static final long GRANULARITY_ONE_DAY_MS = 24 * 60 * 60 * 1000L; // The time-to-live for the cached location private static final long TTL_THIRTY_DAYS_MS = 30 * GRANULARITY_ONE_DAY_MS; + // The maximum number of times to try injecting a location + private static final int MAX_LOCATION_INJECTION_ATTEMPTS = 10; // Used internally for mHandlerThread synchronization private final Object mLock = new Object(); @@ -68,23 +72,26 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB private final CarPowerManagementService mCarPowerManagementService; private final CarPropertyService mCarPropertyService; private final CarPropertyEventListener mCarPropertyEventListener; + private final CarUserManagerHelper mCarUserManagerHelper; private int mTaskCount = 0; private HandlerThread mHandlerThread; private Handler mHandler; public CarLocationService(Context context, CarPowerManagementService carPowerManagementService, - CarPropertyService carPropertyService) { + CarPropertyService carPropertyService, CarUserManagerHelper carUserManagerHelper) { logd("constructed"); mContext = context; mCarPowerManagementService = carPowerManagementService; mCarPropertyService = carPropertyService; mCarPropertyEventListener = new CarPropertyEventListener(); + mCarUserManagerHelper = carUserManagerHelper; } @Override public void init() { logd("init"); IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED); filter.addAction(LocationManager.MODE_CHANGED_ACTION); filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION); @@ -107,17 +114,19 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB writer.println(TAG); writer.println("Context: " + mContext); writer.println("CarPropertyService: " + mCarPropertyService); + writer.println("MAX_LOCATION_INJECTION_ATTEMPTS: " + MAX_LOCATION_INJECTION_ATTEMPTS); } @Override public long onPrepareShutdown(boolean shuttingDown) { logd("onPrepareShutdown " + shuttingDown); asyncOperation(() -> storeLocation()); - return 0; + return 100; } @Override - public void onPowerOn(boolean displayOn) { } + public void onPowerOn(boolean displayOn) { + } @Override public int getWakeupTime() { @@ -129,27 +138,55 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB logd("onReceive " + intent); String action = intent.getAction(); if (action == Intent.ACTION_LOCKED_BOOT_COMPLETED) { - asyncOperation(() -> loadLocation()); - } else { + // If the system user is not headless, then we can inject location as soon as the + // system has completed booting. + if (!mCarUserManagerHelper.isHeadlessSystemUser()) { + logd("not headless on boot complete"); + asyncOperation(() -> loadLocation()); + } + } else 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()); + } + } else if (action == LocationManager.MODE_CHANGED_ACTION + && shouldCheckLocationPermissions()) { LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); - if (action == LocationManager.MODE_CHANGED_ACTION) { - boolean locationEnabled = locationManager.isLocationEnabled(); - logd("isLocationEnabled(): " + locationEnabled); - if (!locationEnabled) { - asyncOperation(() -> deleteCacheFile()); - } - } else if (action == LocationManager.GPS_ENABLED_CHANGE_ACTION) { - boolean gpsEnabled = - locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); - logd("isProviderEnabled('gps'): " + gpsEnabled); - if (!gpsEnabled) { - asyncOperation(() -> deleteCacheFile()); - } + boolean locationEnabled = locationManager.isLocationEnabled(); + logd("isLocationEnabled(): " + locationEnabled); + if (!locationEnabled) { + asyncOperation(() -> deleteCacheFile()); + } + } else if (action == LocationManager.GPS_ENABLED_CHANGE_ACTION + && shouldCheckLocationPermissions()) { + LocationManager locationManager = + (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); + boolean gpsEnabled = + locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); + logd("isProviderEnabled('gps'): " + gpsEnabled); + if (!gpsEnabled) { + asyncOperation(() -> deleteCacheFile()); } } } + /** + * 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()); + } + + /** + * Gets the last known location from the LocationManager and store it in a file. + */ private void storeLocation() { LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); @@ -163,44 +200,44 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB FileOutputStream fos = null; try { fos = atomicFile.startWrite(); - JsonWriter jsonWriter = new JsonWriter(new OutputStreamWriter(fos, "UTF-8")); - jsonWriter.beginObject(); - jsonWriter.name("provider").value(location.getProvider()); - jsonWriter.name("latitude").value(location.getLatitude()); - jsonWriter.name("longitude").value(location.getLongitude()); - if (location.hasAltitude()) { - jsonWriter.name("altitude").value(location.getAltitude()); - } - if (location.hasSpeed()) { - jsonWriter.name("speed").value(location.getSpeed()); - } - if (location.hasBearing()) { - jsonWriter.name("bearing").value(location.getBearing()); - } - if (location.hasAccuracy()) { - jsonWriter.name("accuracy").value(location.getAccuracy()); - } - if (location.hasVerticalAccuracy()) { - jsonWriter.name("verticalAccuracy").value( - location.getVerticalAccuracyMeters()); - } - if (location.hasSpeedAccuracy()) { - jsonWriter.name("speedAccuracy").value( - location.getSpeedAccuracyMetersPerSecond()); - } - if (location.hasBearingAccuracy()) { - jsonWriter.name("bearingAccuracy").value( - location.getBearingAccuracyDegrees()); - } - if (location.isFromMockProvider()) { - jsonWriter.name("isFromMockProvider").value(true); + try (JsonWriter jsonWriter = new JsonWriter(new OutputStreamWriter(fos, "UTF-8"))) { + jsonWriter.beginObject(); + jsonWriter.name("provider").value(location.getProvider()); + jsonWriter.name("latitude").value(location.getLatitude()); + jsonWriter.name("longitude").value(location.getLongitude()); + if (location.hasAltitude()) { + jsonWriter.name("altitude").value(location.getAltitude()); + } + if (location.hasSpeed()) { + jsonWriter.name("speed").value(location.getSpeed()); + } + if (location.hasBearing()) { + jsonWriter.name("bearing").value(location.getBearing()); + } + if (location.hasAccuracy()) { + jsonWriter.name("accuracy").value(location.getAccuracy()); + } + if (location.hasVerticalAccuracy()) { + jsonWriter.name("verticalAccuracy").value( + location.getVerticalAccuracyMeters()); + } + if (location.hasSpeedAccuracy()) { + jsonWriter.name("speedAccuracy").value( + location.getSpeedAccuracyMetersPerSecond()); + } + if (location.hasBearingAccuracy()) { + jsonWriter.name("bearingAccuracy").value( + location.getBearingAccuracyDegrees()); + } + if (location.isFromMockProvider()) { + jsonWriter.name("isFromMockProvider").value(true); + } + long currentTime = location.getTime(); + // Round the time down to only be accurate within one day. + jsonWriter.name("captureTime").value( + currentTime - currentTime % GRANULARITY_ONE_DAY_MS); + jsonWriter.endObject(); } - long currentTime = location.getTime(); - // Round the time down to only be accurate within one day. - jsonWriter.name("captureTime").value( - currentTime - currentTime % GRANULARITY_ONE_DAY_MS); - jsonWriter.endObject(); - jsonWriter.close(); atomicFile.finishWrite(fos); } catch (IOException e) { Log.e(TAG, "Unable to write to disk", e); @@ -209,6 +246,9 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB } } + /** + * Reads a previously stored location and attempts to inject it into the LocationManager. + */ private void loadLocation() { Location location = readLocationFromCacheFile(); logd("Read location from " + location.getTime()); @@ -220,10 +260,7 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB long elapsedTime = SystemClock.elapsedRealtimeNanos(); location.setElapsedRealtimeNanos(elapsedTime); if (location.isComplete()) { - LocationManager locationManager = - (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); - boolean success = locationManager.injectLocation(location); - logd("Injected location " + location + " with result " + success); + injectLocation(location, 1); } } } @@ -231,8 +268,7 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB private Location readLocationFromCacheFile() { Location location = new Location((String) null); AtomicFile atomicFile = new AtomicFile(mContext.getFileStreamPath(FILENAME)); - try { - FileInputStream fis = atomicFile.openRead(); + try (FileInputStream fis = atomicFile.openRead()) { JsonReader reader = new JsonReader(new InputStreamReader(fis, "UTF-8")); reader.beginObject(); while (reader.hasNext()) { @@ -266,7 +302,6 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB } } reader.endObject(); - fis.close(); deleteCacheFile(); } catch (FileNotFoundException e) { Log.d(TAG, "Location cache file not found."); @@ -283,8 +318,33 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB mContext.deleteFile(FILENAME); } + /** + * Attempts to inject the location multiple times in case the LocationManager was not fully + * 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); + logd("Injected location " + location + " with result " + success + " on attempt " + + attemptCount); + if (success) { + return; + } else if (attemptCount <= MAX_LOCATION_INJECTION_ATTEMPTS) { + asyncOperation(() -> { + injectLocation(location, attemptCount + 1); + }, 200 * attemptCount); + } else { + logd("No location injected."); + } + } + @VisibleForTesting void asyncOperation(Runnable operation) { + asyncOperation(operation, 0); + } + + private void asyncOperation(Runnable operation, long delayMillis) { synchronized (mLock) { // Create a new HandlerThread if this is the first task to queue. if (++mTaskCount == 1) { @@ -293,7 +353,7 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB mHandler = new Handler(mHandlerThread.getLooper()); } } - mHandler.post(() -> { + mHandler.postDelayed(() -> { try { operation.run(); } finally { @@ -306,7 +366,7 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB } } } - }); + }, delayMillis); } private static void logd(String msg) { diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java index 9a15631a95..67c50a758c 100644 --- a/service/src/com/android/car/ICarImpl.java +++ b/service/src/com/android/car/ICarImpl.java @@ -123,8 +123,6 @@ public class ICarImpl extends ICar.Stub { mCarInputService = new CarInputService(serviceContext, mHal.getInputHal()); mCarProjectionService = new CarProjectionService(serviceContext, mCarInputService); mGarageModeService = new GarageModeService(mContext, mCarPowerManagementService); - mCarLocationService = new CarLocationService(mContext, mCarPowerManagementService, - mCarPropertyService); mAppFocusService = new AppFocusService(serviceContext, mSystemActivityMonitoringService); mCarAudioService = new CarAudioService(serviceContext); mCarNightService = new CarNightService(serviceContext, mCarPropertyService); @@ -143,6 +141,8 @@ public class ICarImpl extends ICar.Stub { mCarConfigurationService = new CarConfigurationService(serviceContext, new JsonReaderImpl()); mUserManagerHelper = new CarUserManagerHelper(serviceContext); + mCarLocationService = new CarLocationService(mContext, mCarPowerManagementService, + mCarPropertyService, mUserManagerHelper); // Be careful with order. Service depending on other service should be inited later. List<CarServiceBase> allServices = new ArrayList<>(); @@ -153,7 +153,6 @@ public class ICarImpl extends ICar.Stub { allServices.add(mCarUXRestrictionsService); allServices.add(mCarPackageManagerService); allServices.add(mCarInputService); - allServices.add(mCarLocationService); allServices.add(mGarageModeService); allServices.add(mAppFocusService); allServices.add(mCarAudioService); @@ -174,6 +173,7 @@ public class ICarImpl extends ICar.Stub { allServices.add(mCarUserService); } + allServices.add(mCarLocationService); mAllServices = allServices.toArray(new CarServiceBase[allServices.size()]); } |