aboutsummaryrefslogtreecommitdiff
path: root/service/src/com/android
diff options
context:
space:
mode:
authorGregory Clark <gregoryclark@google.com>2018-06-07 16:42:12 -0700
committerGregory Clark <gregoryclark@google.com>2018-06-19 18:19:16 -0700
commita63ba02a9279dc55cc3ef77dbec02c54383afdbe (patch)
tree0352b73e396f1a47b5eabd58cbd93decce1302ec /service/src/com/android
parentcf6d16cbadc4916e67c60573178791cf25b3ff09 (diff)
downloadCar-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.java188
-rw-r--r--service/src/com/android/car/ICarImpl.java6
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()]);
}