summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGregory Clark2018-06-07 18:42:12 -0500
committerGregory Clark2018-06-19 20:19:16 -0500
commita63ba02a9279dc55cc3ef77dbec02c54383afdbe (patch)
tree0352b73e396f1a47b5eabd58cbd93decce1302ec /service
parentcf6d16cbadc4916e67c60573178791cf25b3ff09 (diff)
downloadplatform-packages-services-car-a63ba02a9279dc55cc3ef77dbec02c54383afdbe.tar.gz
platform-packages-services-car-a63ba02a9279dc55cc3ef77dbec02c54383afdbe.tar.xz
platform-packages-services-car-a63ba02a9279dc55cc3ef77dbec02c54383afdbe.zip
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')
-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 6d93b40c..a7aaf035 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;
19import android.car.hardware.CarPropertyValue; 19import android.car.hardware.CarPropertyValue;
20import android.car.hardware.property.CarPropertyEvent; 20import android.car.hardware.property.CarPropertyEvent;
21import android.car.hardware.property.ICarPropertyEventListener; 21import android.car.hardware.property.ICarPropertyEventListener;
22import android.car.user.CarUserManagerHelper;
22import android.content.BroadcastReceiver; 23import android.content.BroadcastReceiver;
23import android.content.Context; 24import android.content.Context;
24import android.content.Intent; 25import android.content.Intent;
@@ -31,6 +32,7 @@ import android.os.Handler;
31import android.os.HandlerThread; 32import android.os.HandlerThread;
32import android.os.RemoteException; 33import android.os.RemoteException;
33import android.os.SystemClock; 34import android.os.SystemClock;
35import android.os.UserHandle;
34import android.util.AtomicFile; 36import android.util.AtomicFile;
35import android.util.JsonReader; 37import android.util.JsonReader;
36import android.util.JsonWriter; 38import android.util.JsonWriter;
@@ -60,6 +62,8 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
60 private static final long GRANULARITY_ONE_DAY_MS = 24 * 60 * 60 * 1000L; 62 private static final long GRANULARITY_ONE_DAY_MS = 24 * 60 * 60 * 1000L;
61 // The time-to-live for the cached location 63 // The time-to-live for the cached location
62 private static final long TTL_THIRTY_DAYS_MS = 30 * GRANULARITY_ONE_DAY_MS; 64 private static final long TTL_THIRTY_DAYS_MS = 30 * GRANULARITY_ONE_DAY_MS;
65 // The maximum number of times to try injecting a location
66 private static final int MAX_LOCATION_INJECTION_ATTEMPTS = 10;
63 67
64 // Used internally for mHandlerThread synchronization 68 // Used internally for mHandlerThread synchronization
65 private final Object mLock = new Object(); 69 private final Object mLock = new Object();
@@ -68,23 +72,26 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
68 private final CarPowerManagementService mCarPowerManagementService; 72 private final CarPowerManagementService mCarPowerManagementService;
69 private final CarPropertyService mCarPropertyService; 73 private final CarPropertyService mCarPropertyService;
70 private final CarPropertyEventListener mCarPropertyEventListener; 74 private final CarPropertyEventListener mCarPropertyEventListener;
75 private final CarUserManagerHelper mCarUserManagerHelper;
71 private int mTaskCount = 0; 76 private int mTaskCount = 0;
72 private HandlerThread mHandlerThread; 77 private HandlerThread mHandlerThread;
73 private Handler mHandler; 78 private Handler mHandler;
74 79
75 public CarLocationService(Context context, CarPowerManagementService carPowerManagementService, 80 public CarLocationService(Context context, CarPowerManagementService carPowerManagementService,
76 CarPropertyService carPropertyService) { 81 CarPropertyService carPropertyService, CarUserManagerHelper carUserManagerHelper) {
77 logd("constructed"); 82 logd("constructed");
78 mContext = context; 83 mContext = context;
79 mCarPowerManagementService = carPowerManagementService; 84 mCarPowerManagementService = carPowerManagementService;
80 mCarPropertyService = carPropertyService; 85 mCarPropertyService = carPropertyService;
81 mCarPropertyEventListener = new CarPropertyEventListener(); 86 mCarPropertyEventListener = new CarPropertyEventListener();
87 mCarUserManagerHelper = carUserManagerHelper;
82 } 88 }
83 89
84 @Override 90 @Override
85 public void init() { 91 public void init() {
86 logd("init"); 92 logd("init");
87 IntentFilter filter = new IntentFilter(); 93 IntentFilter filter = new IntentFilter();
94 filter.addAction(Intent.ACTION_USER_SWITCHED);
88 filter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED); 95 filter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED);
89 filter.addAction(LocationManager.MODE_CHANGED_ACTION); 96 filter.addAction(LocationManager.MODE_CHANGED_ACTION);
90 filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION); 97 filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION);
@@ -107,17 +114,19 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
107 writer.println(TAG); 114 writer.println(TAG);
108 writer.println("Context: " + mContext); 115 writer.println("Context: " + mContext);
109 writer.println("CarPropertyService: " + mCarPropertyService); 116 writer.println("CarPropertyService: " + mCarPropertyService);
117 writer.println("MAX_LOCATION_INJECTION_ATTEMPTS: " + MAX_LOCATION_INJECTION_ATTEMPTS);
110 } 118 }
111 119
112 @Override 120 @Override
113 public long onPrepareShutdown(boolean shuttingDown) { 121 public long onPrepareShutdown(boolean shuttingDown) {
114 logd("onPrepareShutdown " + shuttingDown); 122 logd("onPrepareShutdown " + shuttingDown);
115 asyncOperation(() -> storeLocation()); 123 asyncOperation(() -> storeLocation());
116 return 0; 124 return 100;
117 } 125 }
118 126
119 @Override 127 @Override
120 public void onPowerOn(boolean displayOn) { } 128 public void onPowerOn(boolean displayOn) {
129 }
121 130
122 @Override 131 @Override
123 public int getWakeupTime() { 132 public int getWakeupTime() {
@@ -129,27 +138,55 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
129 logd("onReceive " + intent); 138 logd("onReceive " + intent);
130 String action = intent.getAction(); 139 String action = intent.getAction();
131 if (action == Intent.ACTION_LOCKED_BOOT_COMPLETED) { 140 if (action == Intent.ACTION_LOCKED_BOOT_COMPLETED) {
132 asyncOperation(() -> loadLocation()); 141 // If the system user is not headless, then we can inject location as soon as the
133 } else { 142 // system has completed booting.
143 if (!mCarUserManagerHelper.isHeadlessSystemUser()) {
144 logd("not headless on boot complete");
145 asyncOperation(() -> loadLocation());
146 }
147 } else if (action == Intent.ACTION_USER_SWITCHED) {
148 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
149 logd("USER_SWITCHED: " + userHandle);
150 if (mCarUserManagerHelper.isHeadlessSystemUser()
151 && userHandle > UserHandle.USER_SYSTEM) {
152 asyncOperation(() -> loadLocation());
153 }
154 } else if (action == LocationManager.MODE_CHANGED_ACTION
155 && shouldCheckLocationPermissions()) {
134 LocationManager locationManager = 156 LocationManager locationManager =
135 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); 157 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
136 if (action == LocationManager.MODE_CHANGED_ACTION) { 158 boolean locationEnabled = locationManager.isLocationEnabled();
137 boolean locationEnabled = locationManager.isLocationEnabled(); 159 logd("isLocationEnabled(): " + locationEnabled);
138 logd("isLocationEnabled(): " + locationEnabled); 160 if (!locationEnabled) {
139 if (!locationEnabled) { 161 asyncOperation(() -> deleteCacheFile());
140 asyncOperation(() -> deleteCacheFile()); 162 }
141 } 163 } else if (action == LocationManager.GPS_ENABLED_CHANGE_ACTION
142 } else if (action == LocationManager.GPS_ENABLED_CHANGE_ACTION) { 164 && shouldCheckLocationPermissions()) {
143 boolean gpsEnabled = 165 LocationManager locationManager =
144 locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); 166 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
145 logd("isProviderEnabled('gps'): " + gpsEnabled); 167 boolean gpsEnabled =
146 if (!gpsEnabled) { 168 locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
147 asyncOperation(() -> deleteCacheFile()); 169 logd("isProviderEnabled('gps'): " + gpsEnabled);
148 } 170 if (!gpsEnabled) {
171 asyncOperation(() -> deleteCacheFile());
149 } 172 }
150 } 173 }
151 } 174 }
152 175
176 /**
177 * Tells whether or not we should check location permissions for the sake of deleting the
178 * location cache file when permissions are lacking. If the system user is headless but the
179 * current user is still the system user, then we should not respond to a lack of location
180 * permissions.
181 */
182 private boolean shouldCheckLocationPermissions() {
183 return !(mCarUserManagerHelper.isHeadlessSystemUser()
184 && mCarUserManagerHelper.isCurrentProcessSystemUser());
185 }
186
187 /**
188 * Gets the last known location from the LocationManager and store it in a file.
189 */
153 private void storeLocation() { 190 private void storeLocation() {
154 LocationManager locationManager = 191 LocationManager locationManager =
155 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); 192 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
@@ -163,44 +200,44 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
163 FileOutputStream fos = null; 200 FileOutputStream fos = null;
164 try { 201 try {
165 fos = atomicFile.startWrite(); 202 fos = atomicFile.startWrite();
166 JsonWriter jsonWriter = new JsonWriter(new OutputStreamWriter(fos, "UTF-8")); 203 try (JsonWriter jsonWriter = new JsonWriter(new OutputStreamWriter(fos, "UTF-8"))) {
167 jsonWriter.beginObject(); 204 jsonWriter.beginObject();
168 jsonWriter.name("provider").value(location.getProvider()); 205 jsonWriter.name("provider").value(location.getProvider());
169 jsonWriter.name("latitude").value(location.getLatitude()); 206 jsonWriter.name("latitude").value(location.getLatitude());
170 jsonWriter.name("longitude").value(location.getLongitude()); 207 jsonWriter.name("longitude").value(location.getLongitude());
171 if (location.hasAltitude()) { 208 if (location.hasAltitude()) {
172 jsonWriter.name("altitude").value(location.getAltitude()); 209 jsonWriter.name("altitude").value(location.getAltitude());
173 } 210 }
174 if (location.hasSpeed()) { 211 if (location.hasSpeed()) {
175 jsonWriter.name("speed").value(location.getSpeed()); 212 jsonWriter.name("speed").value(location.getSpeed());
176 } 213 }
177 if (location.hasBearing()) { 214 if (location.hasBearing()) {
178 jsonWriter.name("bearing").value(location.getBearing()); 215 jsonWriter.name("bearing").value(location.getBearing());
179 } 216 }
180 if (location.hasAccuracy()) { 217 if (location.hasAccuracy()) {
181 jsonWriter.name("accuracy").value(location.getAccuracy()); 218 jsonWriter.name("accuracy").value(location.getAccuracy());
182 } 219 }
183 if (location.hasVerticalAccuracy()) { 220 if (location.hasVerticalAccuracy()) {
184 jsonWriter.name("verticalAccuracy").value( 221 jsonWriter.name("verticalAccuracy").value(
185 location.getVerticalAccuracyMeters()); 222 location.getVerticalAccuracyMeters());
186 } 223 }
187 if (location.hasSpeedAccuracy()) { 224 if (location.hasSpeedAccuracy()) {
188 jsonWriter.name("speedAccuracy").value( 225 jsonWriter.name("speedAccuracy").value(
189 location.getSpeedAccuracyMetersPerSecond()); 226 location.getSpeedAccuracyMetersPerSecond());
190 } 227 }
191 if (location.hasBearingAccuracy()) { 228 if (location.hasBearingAccuracy()) {
192 jsonWriter.name("bearingAccuracy").value( 229 jsonWriter.name("bearingAccuracy").value(
193 location.getBearingAccuracyDegrees()); 230 location.getBearingAccuracyDegrees());
194 } 231 }
195 if (location.isFromMockProvider()) { 232 if (location.isFromMockProvider()) {
196 jsonWriter.name("isFromMockProvider").value(true); 233 jsonWriter.name("isFromMockProvider").value(true);
234 }
235 long currentTime = location.getTime();
236 // Round the time down to only be accurate within one day.
237 jsonWriter.name("captureTime").value(
238 currentTime - currentTime % GRANULARITY_ONE_DAY_MS);
239 jsonWriter.endObject();
197 } 240 }
198 long currentTime = location.getTime();
199 // Round the time down to only be accurate within one day.
200 jsonWriter.name("captureTime").value(
201 currentTime - currentTime % GRANULARITY_ONE_DAY_MS);
202 jsonWriter.endObject();
203 jsonWriter.close();
204 atomicFile.finishWrite(fos); 241 atomicFile.finishWrite(fos);
205 } catch (IOException e) { 242 } catch (IOException e) {
206 Log.e(TAG, "Unable to write to disk", e); 243 Log.e(TAG, "Unable to write to disk", e);
@@ -209,6 +246,9 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
209 } 246 }
210 } 247 }
211 248
249 /**
250 * Reads a previously stored location and attempts to inject it into the LocationManager.
251 */
212 private void loadLocation() { 252 private void loadLocation() {
213 Location location = readLocationFromCacheFile(); 253 Location location = readLocationFromCacheFile();
214 logd("Read location from " + location.getTime()); 254 logd("Read location from " + location.getTime());
@@ -220,10 +260,7 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
220 long elapsedTime = SystemClock.elapsedRealtimeNanos(); 260 long elapsedTime = SystemClock.elapsedRealtimeNanos();
221 location.setElapsedRealtimeNanos(elapsedTime); 261 location.setElapsedRealtimeNanos(elapsedTime);
222 if (location.isComplete()) { 262 if (location.isComplete()) {
223 LocationManager locationManager = 263 injectLocation(location, 1);
224 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
225 boolean success = locationManager.injectLocation(location);
226 logd("Injected location " + location + " with result " + success);
227 } 264 }
228 } 265 }
229 } 266 }
@@ -231,8 +268,7 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
231 private Location readLocationFromCacheFile() { 268 private Location readLocationFromCacheFile() {
232 Location location = new Location((String) null); 269 Location location = new Location((String) null);
233 AtomicFile atomicFile = new AtomicFile(mContext.getFileStreamPath(FILENAME)); 270 AtomicFile atomicFile = new AtomicFile(mContext.getFileStreamPath(FILENAME));
234 try { 271 try (FileInputStream fis = atomicFile.openRead()) {
235 FileInputStream fis = atomicFile.openRead();
236 JsonReader reader = new JsonReader(new InputStreamReader(fis, "UTF-8")); 272 JsonReader reader = new JsonReader(new InputStreamReader(fis, "UTF-8"));
237 reader.beginObject(); 273 reader.beginObject();
238 while (reader.hasNext()) { 274 while (reader.hasNext()) {
@@ -266,7 +302,6 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
266 } 302 }
267 } 303 }
268 reader.endObject(); 304 reader.endObject();
269 fis.close();
270 deleteCacheFile(); 305 deleteCacheFile();
271 } catch (FileNotFoundException e) { 306 } catch (FileNotFoundException e) {
272 Log.d(TAG, "Location cache file not found."); 307 Log.d(TAG, "Location cache file not found.");
@@ -283,8 +318,33 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
283 mContext.deleteFile(FILENAME); 318 mContext.deleteFile(FILENAME);
284 } 319 }
285 320
321 /**
322 * Attempts to inject the location multiple times in case the LocationManager was not fully
323 * initialized or has not updated its handle to the current user yet.
324 */
325 private void injectLocation(Location location, int attemptCount) {
326 LocationManager locationManager =
327 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
328 boolean success = locationManager.injectLocation(location);
329 logd("Injected location " + location + " with result " + success + " on attempt "
330 + attemptCount);
331 if (success) {
332 return;
333 } else if (attemptCount <= MAX_LOCATION_INJECTION_ATTEMPTS) {
334 asyncOperation(() -> {
335 injectLocation(location, attemptCount + 1);
336 }, 200 * attemptCount);
337 } else {
338 logd("No location injected.");
339 }
340 }
341
286 @VisibleForTesting 342 @VisibleForTesting
287 void asyncOperation(Runnable operation) { 343 void asyncOperation(Runnable operation) {
344 asyncOperation(operation, 0);
345 }
346
347 private void asyncOperation(Runnable operation, long delayMillis) {
288 synchronized (mLock) { 348 synchronized (mLock) {
289 // Create a new HandlerThread if this is the first task to queue. 349 // Create a new HandlerThread if this is the first task to queue.
290 if (++mTaskCount == 1) { 350 if (++mTaskCount == 1) {
@@ -293,7 +353,7 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
293 mHandler = new Handler(mHandlerThread.getLooper()); 353 mHandler = new Handler(mHandlerThread.getLooper());
294 } 354 }
295 } 355 }
296 mHandler.post(() -> { 356 mHandler.postDelayed(() -> {
297 try { 357 try {
298 operation.run(); 358 operation.run();
299 } finally { 359 } finally {
@@ -306,7 +366,7 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
306 } 366 }
307 } 367 }
308 } 368 }
309 }); 369 }, delayMillis);
310 } 370 }
311 371
312 private static void logd(String msg) { 372 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 9a15631a..67c50a75 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 {
123 mCarInputService = new CarInputService(serviceContext, mHal.getInputHal()); 123 mCarInputService = new CarInputService(serviceContext, mHal.getInputHal());
124 mCarProjectionService = new CarProjectionService(serviceContext, mCarInputService); 124 mCarProjectionService = new CarProjectionService(serviceContext, mCarInputService);
125 mGarageModeService = new GarageModeService(mContext, mCarPowerManagementService); 125 mGarageModeService = new GarageModeService(mContext, mCarPowerManagementService);
126 mCarLocationService = new CarLocationService(mContext, mCarPowerManagementService,
127 mCarPropertyService);
128 mAppFocusService = new AppFocusService(serviceContext, mSystemActivityMonitoringService); 126 mAppFocusService = new AppFocusService(serviceContext, mSystemActivityMonitoringService);
129 mCarAudioService = new CarAudioService(serviceContext); 127 mCarAudioService = new CarAudioService(serviceContext);
130 mCarNightService = new CarNightService(serviceContext, mCarPropertyService); 128 mCarNightService = new CarNightService(serviceContext, mCarPropertyService);
@@ -143,6 +141,8 @@ public class ICarImpl extends ICar.Stub {
143 mCarConfigurationService = 141 mCarConfigurationService =
144 new CarConfigurationService(serviceContext, new JsonReaderImpl()); 142 new CarConfigurationService(serviceContext, new JsonReaderImpl());
145 mUserManagerHelper = new CarUserManagerHelper(serviceContext); 143 mUserManagerHelper = new CarUserManagerHelper(serviceContext);
144 mCarLocationService = new CarLocationService(mContext, mCarPowerManagementService,
145 mCarPropertyService, mUserManagerHelper);
146 146
147 // Be careful with order. Service depending on other service should be inited later. 147 // Be careful with order. Service depending on other service should be inited later.
148 List<CarServiceBase> allServices = new ArrayList<>(); 148 List<CarServiceBase> allServices = new ArrayList<>();
@@ -153,7 +153,6 @@ public class ICarImpl extends ICar.Stub {
153 allServices.add(mCarUXRestrictionsService); 153 allServices.add(mCarUXRestrictionsService);
154 allServices.add(mCarPackageManagerService); 154 allServices.add(mCarPackageManagerService);
155 allServices.add(mCarInputService); 155 allServices.add(mCarInputService);
156 allServices.add(mCarLocationService);
157 allServices.add(mGarageModeService); 156 allServices.add(mGarageModeService);
158 allServices.add(mAppFocusService); 157 allServices.add(mAppFocusService);
159 allServices.add(mCarAudioService); 158 allServices.add(mCarAudioService);
@@ -174,6 +173,7 @@ public class ICarImpl extends ICar.Stub {
174 allServices.add(mCarUserService); 173 allServices.add(mCarUserService);
175 } 174 }
176 175
176 allServices.add(mCarLocationService);
177 mAllServices = allServices.toArray(new CarServiceBase[allServices.size()]); 177 mAllServices = allServices.toArray(new CarServiceBase[allServices.size()]);
178 } 178 }
179 179