blob: 6973033e1c58f6aae68c2a333efb7baab08f90d6 [file] [log] [blame]
Gregory Clarkd8136062017-12-11 14:27:53 -08001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.car;
18
Gregory Clark55620c12019-06-03 17:42:58 -070019import android.app.ActivityManager;
Gregory Clark55620c12019-06-03 17:42:58 -070020import android.car.ILocationManagerProxy;
Eric Jeonga84a6822019-09-17 15:46:38 -070021import android.car.IPerUserCarService;
Gregory Clark18a0ef92019-05-23 20:00:49 -070022import android.car.drivingstate.CarDrivingStateEvent;
23import android.car.drivingstate.ICarDrivingStateChangeListener;
Serik Beketayev68920542018-09-06 13:29:58 -070024import android.car.hardware.power.CarPowerManager;
25import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
Jim Kayee5133162019-04-22 12:50:27 -070026import android.car.hardware.power.CarPowerManager.CarPowerStateListenerWithCompletion;
Gregory Clarke70eb5b2018-02-07 18:29:24 -080027import android.content.BroadcastReceiver;
Gregory Clarkd8136062017-12-11 14:27:53 -080028import android.content.Context;
Gregory Clarke70eb5b2018-02-07 18:29:24 -080029import android.content.Intent;
30import android.content.IntentFilter;
Gregory Clarkd8136062017-12-11 14:27:53 -080031import android.location.Location;
32import android.location.LocationManager;
Gregory Clark3f9b56d2018-01-31 13:02:41 -080033import android.os.Handler;
34import android.os.HandlerThread;
Gregory Clark55620c12019-06-03 17:42:58 -070035import android.os.RemoteException;
Gregory Clark756ed812018-02-06 18:19:27 -080036import android.os.SystemClock;
Gregory Clarka63ba022018-06-07 16:42:12 -070037import android.os.UserHandle;
Anthony Hugh5f2465e2019-10-08 15:07:01 -070038import android.os.UserManager;
Gregory Clarkd8136062017-12-11 14:27:53 -080039import android.util.AtomicFile;
Felipe Leme176a5fd2021-01-20 15:48:33 -080040import android.util.IndentingPrintWriter;
Gregory Clarkd8136062017-12-11 14:27:53 -080041import android.util.JsonReader;
42import android.util.JsonWriter;
Eric Jeongbd5fb562020-12-21 13:49:40 -080043import android.util.Slog;
Gregory Clarkd8136062017-12-11 14:27:53 -080044
Gregory Clarka440e812019-02-14 16:05:51 -080045import com.android.car.systeminterface.SystemInterface;
Gregory Clark3f9b56d2018-01-31 13:02:41 -080046import com.android.internal.annotations.VisibleForTesting;
47
Gregory Clarka440e812019-02-14 16:05:51 -080048import java.io.File;
Gregory Clarkd8136062017-12-11 14:27:53 -080049import java.io.FileInputStream;
Gregory Clark3f9b56d2018-01-31 13:02:41 -080050import java.io.FileNotFoundException;
Gregory Clarkd8136062017-12-11 14:27:53 -080051import java.io.FileOutputStream;
52import java.io.IOException;
53import java.io.InputStreamReader;
54import java.io.OutputStreamWriter;
Serik Beketayev68920542018-09-06 13:29:58 -070055import java.util.concurrent.CompletableFuture;
Gregory Clarkd8136062017-12-11 14:27:53 -080056
57/**
58 * This service stores the last known location from {@link LocationManager} when a car is parked
59 * and restores the location when the car is powered on.
Gregory Clarkd8136062017-12-11 14:27:53 -080060 */
Gregory Clark18a0ef92019-05-23 20:00:49 -070061public class CarLocationService extends BroadcastReceiver implements CarServiceBase,
62 CarPowerStateListenerWithCompletion {
Mayank Garg72c71d22021-02-03 23:54:45 -080063 private static final String TAG = CarLog.tagFor(CarLocationService.class);
Gregory Clark67259c22018-03-09 15:30:21 -080064 private static final String FILENAME = "location_cache.json";
Gregory Clark67259c22018-03-09 15:30:21 -080065 // The accuracy for the stored timestamp
66 private static final long GRANULARITY_ONE_DAY_MS = 24 * 60 * 60 * 1000L;
67 // The time-to-live for the cached location
68 private static final long TTL_THIRTY_DAYS_MS = 30 * GRANULARITY_ONE_DAY_MS;
Gregory Clarka63ba022018-06-07 16:42:12 -070069 // The maximum number of times to try injecting a location
70 private static final int MAX_LOCATION_INJECTION_ATTEMPTS = 10;
Gregory Clarkd8136062017-12-11 14:27:53 -080071
Gregory Clarkc960e5a2020-01-24 20:45:24 -080072 // Constants for location serialization.
73 private static final String PROVIDER = "provider";
74 private static final String LATITUDE = "latitude";
75 private static final String LONGITUDE = "longitude";
76 private static final String ALTITUDE = "altitude";
77 private static final String SPEED = "speed";
78 private static final String BEARING = "bearing";
79 private static final String ACCURACY = "accuracy";
80 private static final String VERTICAL_ACCURACY = "verticalAccuracy";
81 private static final String SPEED_ACCURACY = "speedAccuracy";
82 private static final String BEARING_ACCURACY = "bearingAccuracy";
83 private static final String IS_FROM_MOCK_PROVIDER = "isFromMockProvider";
84 private static final String CAPTURE_TIME = "captureTime";
85
Gregory Clark3f9b56d2018-01-31 13:02:41 -080086 // Used internally for mHandlerThread synchronization
Gregory Clarkd8136062017-12-11 14:27:53 -080087 private final Object mLock = new Object();
88
Gregory Clark55620c12019-06-03 17:42:58 -070089 // Used internally for mILocationManagerProxy synchronization
90 private final Object mLocationManagerProxyLock = new Object();
91
Gregory Clarkd8136062017-12-11 14:27:53 -080092 private final Context mContext;
Keun young Parkb241d022020-04-20 20:31:34 -070093 private final HandlerThread mHandlerThread = CarServiceUtils.getHandlerThread(
94 getClass().getSimpleName());
95 private final Handler mHandler = new Handler(mHandlerThread.getLooper());
96
Serik Beketayev68920542018-09-06 13:29:58 -070097 private CarPowerManager mCarPowerManager;
Gregory Clark18a0ef92019-05-23 20:00:49 -070098 private CarDrivingStateService mCarDrivingStateService;
Gregory Clark55620c12019-06-03 17:42:58 -070099 private PerUserCarServiceHelper mPerUserCarServiceHelper;
100
101 // Allows us to interact with the {@link LocationManager} as the foreground user.
102 private ILocationManagerProxy mILocationManagerProxy;
103
104 // Maintains mILocationManagerProxy for the current foreground user.
105 private final PerUserCarServiceHelper.ServiceCallback mUserServiceCallback =
106 new PerUserCarServiceHelper.ServiceCallback() {
107 @Override
Eric Jeonga84a6822019-09-17 15:46:38 -0700108 public void onServiceConnected(IPerUserCarService perUserCarService) {
Gregory Clark55620c12019-06-03 17:42:58 -0700109 logd("Connected to PerUserCarService");
Eric Jeonga84a6822019-09-17 15:46:38 -0700110 if (perUserCarService == null) {
111 logd("IPerUserCarService is null. Cannot get location manager proxy");
Gregory Clark55620c12019-06-03 17:42:58 -0700112 return;
113 }
114 synchronized (mLocationManagerProxyLock) {
115 try {
Eric Jeonga84a6822019-09-17 15:46:38 -0700116 mILocationManagerProxy = perUserCarService.getLocationManagerProxy();
Gregory Clark55620c12019-06-03 17:42:58 -0700117 } catch (RemoteException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800118 Slog.e(TAG, "RemoteException from IPerUserCarService", e);
Gregory Clark55620c12019-06-03 17:42:58 -0700119 return;
120 }
121 }
122 int currentUser = ActivityManager.getCurrentUser();
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800123 logd("Current user: %s", currentUser);
Anthony Hugh5f2465e2019-10-08 15:07:01 -0700124 if (UserManager.isHeadlessSystemUserMode()
Gregory Clark55620c12019-06-03 17:42:58 -0700125 && currentUser > UserHandle.USER_SYSTEM) {
126 asyncOperation(() -> loadLocation());
127 }
128 }
129
130 @Override
131 public void onPreUnbind() {
Gregory Clarkcebacef2019-07-09 16:27:36 -0700132 logd("Before Unbinding from PerUserCarService");
Gregory Clark55620c12019-06-03 17:42:58 -0700133 synchronized (mLocationManagerProxyLock) {
134 mILocationManagerProxy = null;
135 }
136 }
137
138 @Override
139 public void onServiceDisconnected() {
140 logd("Disconnected from PerUserCarService");
141 synchronized (mLocationManagerProxyLock) {
142 mILocationManagerProxy = null;
143 }
144 }
145 };
146
147 private final ICarDrivingStateChangeListener mICarDrivingStateChangeEventListener =
148 new ICarDrivingStateChangeListener.Stub() {
149 @Override
150 public void onDrivingStateChanged(CarDrivingStateEvent event) {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800151 logd("onDrivingStateChanged: %s", event);
Gregory Clark55620c12019-06-03 17:42:58 -0700152 if (event != null
153 && event.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING) {
154 deleteCacheFile();
155 if (mCarDrivingStateService != null) {
156 mCarDrivingStateService.unregisterDrivingStateChangeListener(
157 mICarDrivingStateChangeEventListener);
158 }
159 }
160 }
161 };
Gregory Clarkd8136062017-12-11 14:27:53 -0800162
Anthony Hugh5f2465e2019-10-08 15:07:01 -0700163 public CarLocationService(Context context) {
Gregory Clarkd8136062017-12-11 14:27:53 -0800164 logd("constructed");
165 mContext = context;
Gregory Clarkd8136062017-12-11 14:27:53 -0800166 }
167
168 @Override
169 public void init() {
170 logd("init");
Gregory Clarke70eb5b2018-02-07 18:29:24 -0800171 IntentFilter filter = new IntentFilter();
Gregory Clark29b7f0f2018-02-16 17:53:40 -0800172 filter.addAction(LocationManager.MODE_CHANGED_ACTION);
Gregory Clarke70eb5b2018-02-07 18:29:24 -0800173 mContext.registerReceiver(this, filter);
Gregory Clark18a0ef92019-05-23 20:00:49 -0700174 mCarDrivingStateService = CarLocalServices.getService(CarDrivingStateService.class);
175 if (mCarDrivingStateService != null) {
176 CarDrivingStateEvent event = mCarDrivingStateService.getCurrentDrivingState();
177 if (event != null && event.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING) {
178 deleteCacheFile();
179 } else {
180 mCarDrivingStateService.registerDrivingStateChangeListener(
181 mICarDrivingStateChangeEventListener);
182 }
183 }
Keun young Parka5fa4782019-04-16 18:56:27 -0700184 mCarPowerManager = CarLocalServices.createCarPowerManager(mContext);
185 if (mCarPowerManager != null) { // null case happens for testing.
Jim Kayee5133162019-04-22 12:50:27 -0700186 mCarPowerManager.setListenerWithCompletion(CarLocationService.this);
Keun young Parka5fa4782019-04-16 18:56:27 -0700187 }
Gregory Clark55620c12019-06-03 17:42:58 -0700188 mPerUserCarServiceHelper = CarLocalServices.getService(PerUserCarServiceHelper.class);
189 if (mPerUserCarServiceHelper != null) {
190 mPerUserCarServiceHelper.registerServiceCallback(mUserServiceCallback);
191 }
Gregory Clarkd8136062017-12-11 14:27:53 -0800192 }
193
194 @Override
195 public void release() {
196 logd("release");
Keun young Parka5fa4782019-04-16 18:56:27 -0700197 if (mCarPowerManager != null) {
198 mCarPowerManager.clearListener();
199 }
Gregory Clark18a0ef92019-05-23 20:00:49 -0700200 if (mCarDrivingStateService != null) {
201 mCarDrivingStateService.unregisterDrivingStateChangeListener(
202 mICarDrivingStateChangeEventListener);
203 }
Gregory Clark55620c12019-06-03 17:42:58 -0700204 if (mPerUserCarServiceHelper != null) {
205 mPerUserCarServiceHelper.unregisterServiceCallback(mUserServiceCallback);
206 }
Gregory Clarke70eb5b2018-02-07 18:29:24 -0800207 mContext.unregisterReceiver(this);
Gregory Clarkd8136062017-12-11 14:27:53 -0800208 }
209
210 @Override
Felipe Leme176a5fd2021-01-20 15:48:33 -0800211 public void dump(IndentingPrintWriter writer) {
Gregory Clarkd8136062017-12-11 14:27:53 -0800212 writer.println(TAG);
Felipe Leme176a5fd2021-01-20 15:48:33 -0800213 mPerUserCarServiceHelper.dump(writer);
214 writer.printf("Context: %s\n", mContext);
215 writer.printf("MAX_LOCATION_INJECTION_ATTEMPTS: %d\n", MAX_LOCATION_INJECTION_ATTEMPTS);
Gregory Clarkd8136062017-12-11 14:27:53 -0800216 }
217
218 @Override
Serik Beketayev68920542018-09-06 13:29:58 -0700219 public void onStateChanged(int state, CompletableFuture<Void> future) {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800220 logd("onStateChanged: %s", state);
Serik Beketayev68920542018-09-06 13:29:58 -0700221 switch (state) {
Steve Paik07db5ed2018-09-24 16:48:52 -0700222 case CarPowerStateListener.SHUTDOWN_PREPARE:
Gregory Clark5b9a9cd2018-09-10 13:21:22 -0700223 asyncOperation(() -> {
224 storeLocation();
225 // Notify the CarPowerManager that it may proceed to shutdown or suspend.
Gregory Clark71ed6722018-09-24 13:10:16 -0700226 if (future != null) {
227 future.complete(null);
228 }
Gregory Clark5b9a9cd2018-09-10 13:21:22 -0700229 });
Serik Beketayev68920542018-09-06 13:29:58 -0700230 break;
Gregory Clark63a4ad22019-05-23 18:36:33 -0700231 case CarPowerStateListener.SUSPEND_EXIT:
Gregory Clarkdfb084f2019-07-15 16:41:57 -0700232 if (mCarDrivingStateService != null) {
233 CarDrivingStateEvent event = mCarDrivingStateService.getCurrentDrivingState();
234 if (event != null
235 && event.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING) {
236 deleteCacheFile();
237 } else {
238 logd("Registering to receive driving state.");
239 mCarDrivingStateService.registerDrivingStateChangeListener(
240 mICarDrivingStateChangeEventListener);
241 }
242 }
Gregory Clark63a4ad22019-05-23 18:36:33 -0700243 if (future != null) {
244 future.complete(null);
245 }
Steve Paik07db5ed2018-09-24 16:48:52 -0700246 default:
Gregory Clark5b9a9cd2018-09-10 13:21:22 -0700247 // This service does not need to do any work for these events but should still
248 // notify the CarPowerManager that it may proceed.
Gregory Clark71ed6722018-09-24 13:10:16 -0700249 if (future != null) {
250 future.complete(null);
251 }
Serik Beketayev68920542018-09-06 13:29:58 -0700252 break;
253 }
Gregory Clark26fa6012018-03-14 18:38:56 -0700254 }
255
Gregory Clark26fa6012018-03-14 18:38:56 -0700256 @Override
Gregory Clarke70eb5b2018-02-07 18:29:24 -0800257 public void onReceive(Context context, Intent intent) {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800258 logd("onReceive %s", intent);
Gregory Clark55620c12019-06-03 17:42:58 -0700259 // If the system user is headless but the current user is still the system user, then we
260 // should not delete the location cache file due to missing location permissions.
261 if (isCurrentUserHeadlessSystemUser()) {
262 logd("Current user is headless system user.");
263 return;
264 }
265 synchronized (mLocationManagerProxyLock) {
266 if (mILocationManagerProxy == null) {
267 logd("Null location manager.");
268 return;
Gregory Clarka63ba022018-06-07 16:42:12 -0700269 }
Gregory Clark55620c12019-06-03 17:42:58 -0700270 String action = intent.getAction();
271 try {
272 if (action == LocationManager.MODE_CHANGED_ACTION) {
273 boolean locationEnabled = mILocationManagerProxy.isLocationEnabled();
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800274 logd("isLocationEnabled(): %s", locationEnabled);
Gregory Clark55620c12019-06-03 17:42:58 -0700275 if (!locationEnabled) {
276 deleteCacheFile();
277 }
278 } else {
279 logd("Unexpected intent.");
280 }
281 } catch (RemoteException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800282 Slog.e(TAG, "RemoteException from ILocationManagerProxy", e);
Gregory Clark29b7f0f2018-02-16 17:53:40 -0800283 }
284 }
Gregory Clarkd8136062017-12-11 14:27:53 -0800285 }
286
Gregory Clark55620c12019-06-03 17:42:58 -0700287 /** Tells whether the current foreground user is the headless system user. */
288 private boolean isCurrentUserHeadlessSystemUser() {
289 int currentUserId = ActivityManager.getCurrentUser();
Anthony Hugh5f2465e2019-10-08 15:07:01 -0700290 return UserManager.isHeadlessSystemUserMode()
Anthony Hugh9932a252019-06-12 16:19:56 -0700291 && currentUserId == UserHandle.USER_SYSTEM;
Gregory Clarka63ba022018-06-07 16:42:12 -0700292 }
293
294 /**
Gregory Clark55620c12019-06-03 17:42:58 -0700295 * Gets the last known location from the location manager proxy and store it in a file.
Gregory Clarka63ba022018-06-07 16:42:12 -0700296 */
Gregory Clarkd8136062017-12-11 14:27:53 -0800297 private void storeLocation() {
Gregory Clark55620c12019-06-03 17:42:58 -0700298 Location location = null;
299 synchronized (mLocationManagerProxyLock) {
300 if (mILocationManagerProxy == null) {
301 logd("Null location manager proxy.");
302 return;
303 }
304 try {
305 location = mILocationManagerProxy.getLastKnownLocation(
306 LocationManager.GPS_PROVIDER);
307 } catch (RemoteException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800308 Slog.e(TAG, "RemoteException from ILocationManagerProxy", e);
Gregory Clark55620c12019-06-03 17:42:58 -0700309 }
310 }
Gregory Clarkd8136062017-12-11 14:27:53 -0800311 if (location == null) {
312 logd("Not storing null location");
313 } else {
Pierre Fite-Georgelb189e2b2019-10-31 16:50:04 -0700314 logd("Storing location");
Gregory Clarka440e812019-02-14 16:05:51 -0800315 AtomicFile atomicFile = new AtomicFile(getLocationCacheFile());
Gregory Clarkd8136062017-12-11 14:27:53 -0800316 FileOutputStream fos = null;
317 try {
Gregory Clark3f9b56d2018-01-31 13:02:41 -0800318 fos = atomicFile.startWrite();
Gregory Clarka63ba022018-06-07 16:42:12 -0700319 try (JsonWriter jsonWriter = new JsonWriter(new OutputStreamWriter(fos, "UTF-8"))) {
320 jsonWriter.beginObject();
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800321 jsonWriter.name(PROVIDER).value(location.getProvider());
322 jsonWriter.name(LATITUDE).value(location.getLatitude());
323 jsonWriter.name(LONGITUDE).value(location.getLongitude());
Gregory Clarka63ba022018-06-07 16:42:12 -0700324 if (location.hasAltitude()) {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800325 jsonWriter.name(ALTITUDE).value(location.getAltitude());
Gregory Clarka63ba022018-06-07 16:42:12 -0700326 }
327 if (location.hasSpeed()) {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800328 jsonWriter.name(SPEED).value(location.getSpeed());
Gregory Clarka63ba022018-06-07 16:42:12 -0700329 }
330 if (location.hasBearing()) {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800331 jsonWriter.name(BEARING).value(location.getBearing());
Gregory Clarka63ba022018-06-07 16:42:12 -0700332 }
333 if (location.hasAccuracy()) {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800334 jsonWriter.name(ACCURACY).value(location.getAccuracy());
Gregory Clarka63ba022018-06-07 16:42:12 -0700335 }
336 if (location.hasVerticalAccuracy()) {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800337 jsonWriter.name(VERTICAL_ACCURACY).value(
Gregory Clarka63ba022018-06-07 16:42:12 -0700338 location.getVerticalAccuracyMeters());
339 }
340 if (location.hasSpeedAccuracy()) {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800341 jsonWriter.name(SPEED_ACCURACY).value(
Gregory Clarka63ba022018-06-07 16:42:12 -0700342 location.getSpeedAccuracyMetersPerSecond());
343 }
344 if (location.hasBearingAccuracy()) {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800345 jsonWriter.name(BEARING_ACCURACY).value(
Gregory Clarka63ba022018-06-07 16:42:12 -0700346 location.getBearingAccuracyDegrees());
347 }
348 if (location.isFromMockProvider()) {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800349 jsonWriter.name(IS_FROM_MOCK_PROVIDER).value(true);
Gregory Clarka63ba022018-06-07 16:42:12 -0700350 }
351 long currentTime = location.getTime();
352 // Round the time down to only be accurate within one day.
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800353 jsonWriter.name(CAPTURE_TIME).value(
Gregory Clarka63ba022018-06-07 16:42:12 -0700354 currentTime - currentTime % GRANULARITY_ONE_DAY_MS);
355 jsonWriter.endObject();
Gregory Clarkd8136062017-12-11 14:27:53 -0800356 }
Gregory Clark3f9b56d2018-01-31 13:02:41 -0800357 atomicFile.finishWrite(fos);
Gregory Clarkd8136062017-12-11 14:27:53 -0800358 } catch (IOException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800359 Slog.e(TAG, "Unable to write to disk", e);
Gregory Clarkd8136062017-12-11 14:27:53 -0800360 atomicFile.failWrite(fos);
361 }
362 }
363 }
364
Gregory Clarka63ba022018-06-07 16:42:12 -0700365 /**
Gregory Clark55620c12019-06-03 17:42:58 -0700366 * Reads a previously stored location and attempts to inject it into the location manager proxy.
Gregory Clarka63ba022018-06-07 16:42:12 -0700367 */
Gregory Clarkd8136062017-12-11 14:27:53 -0800368 private void loadLocation() {
Gregory Clark67259c22018-03-09 15:30:21 -0800369 Location location = readLocationFromCacheFile();
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800370 logd("Read location from timestamp %s", location.getTime());
Gregory Clark67259c22018-03-09 15:30:21 -0800371 long currentTime = System.currentTimeMillis();
372 if (location.getTime() + TTL_THIRTY_DAYS_MS < currentTime) {
373 logd("Location expired.");
Gregory Clark63a4ad22019-05-23 18:36:33 -0700374 deleteCacheFile();
Gregory Clark67259c22018-03-09 15:30:21 -0800375 } else {
376 location.setTime(currentTime);
377 long elapsedTime = SystemClock.elapsedRealtimeNanos();
378 location.setElapsedRealtimeNanos(elapsedTime);
379 if (location.isComplete()) {
Gregory Clarka63ba022018-06-07 16:42:12 -0700380 injectLocation(location, 1);
Gregory Clark67259c22018-03-09 15:30:21 -0800381 }
382 }
383 }
384
385 private Location readLocationFromCacheFile() {
Gregory Clarkd8136062017-12-11 14:27:53 -0800386 Location location = new Location((String) null);
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800387 File file = getLocationCacheFile();
388 AtomicFile atomicFile = new AtomicFile(file);
Gregory Clarka63ba022018-06-07 16:42:12 -0700389 try (FileInputStream fis = atomicFile.openRead()) {
Gregory Clark3f9b56d2018-01-31 13:02:41 -0800390 JsonReader reader = new JsonReader(new InputStreamReader(fis, "UTF-8"));
391 reader.beginObject();
392 while (reader.hasNext()) {
393 String name = reader.nextName();
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800394 switch (name) {
395 case PROVIDER:
396 location.setProvider(reader.nextString());
397 break;
398 case LATITUDE:
399 location.setLatitude(reader.nextDouble());
400 break;
401 case LONGITUDE:
402 location.setLongitude(reader.nextDouble());
403 break;
404 case ALTITUDE:
405 location.setAltitude(reader.nextDouble());
406 break;
407 case SPEED:
408 location.setSpeed((float) reader.nextDouble());
409 break;
410 case BEARING:
411 location.setBearing((float) reader.nextDouble());
412 break;
413 case ACCURACY:
414 location.setAccuracy((float) reader.nextDouble());
415 break;
416 case VERTICAL_ACCURACY:
417 location.setVerticalAccuracyMeters((float) reader.nextDouble());
418 break;
419 case SPEED_ACCURACY:
420 location.setSpeedAccuracyMetersPerSecond((float) reader.nextDouble());
421 break;
422 case BEARING_ACCURACY:
423 location.setBearingAccuracyDegrees((float) reader.nextDouble());
424 break;
425 case IS_FROM_MOCK_PROVIDER:
426 location.setIsFromMockProvider(reader.nextBoolean());
427 break;
428 case CAPTURE_TIME:
429 location.setTime(reader.nextLong());
430 break;
431 default:
Eric Jeongbd5fb562020-12-21 13:49:40 -0800432 Slog.w(TAG, "Unrecognized key: " + name);
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800433 reader.skipValue();
Gregory Clarkd8136062017-12-11 14:27:53 -0800434 }
Gregory Clarkd8136062017-12-11 14:27:53 -0800435 }
Gregory Clark3f9b56d2018-01-31 13:02:41 -0800436 reader.endObject();
Gregory Clark3f9b56d2018-01-31 13:02:41 -0800437 } catch (FileNotFoundException e) {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800438 logd("Location cache file not found: %s", file);
Gregory Clarkd8136062017-12-11 14:27:53 -0800439 } catch (IOException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800440 Slog.e(TAG, "Unable to read from disk", e);
Gregory Clarkd8136062017-12-11 14:27:53 -0800441 } catch (NumberFormatException | IllegalStateException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800442 Slog.e(TAG, "Unexpected format", e);
Gregory Clarkd8136062017-12-11 14:27:53 -0800443 }
Gregory Clark67259c22018-03-09 15:30:21 -0800444 return location;
Gregory Clarkd8136062017-12-11 14:27:53 -0800445 }
446
Gregory Clark29b7f0f2018-02-16 17:53:40 -0800447 private void deleteCacheFile() {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800448 File file = getLocationCacheFile();
449 boolean deleted = file.delete();
450 if (deleted) {
451 logd("Successfully deleted cache file at %s", file);
452 } else {
453 logd("Failed to delete cache file at %s", file);
454 }
Gregory Clark29b7f0f2018-02-16 17:53:40 -0800455 }
456
Gregory Clarka63ba022018-06-07 16:42:12 -0700457 /**
458 * Attempts to inject the location multiple times in case the LocationManager was not fully
459 * initialized or has not updated its handle to the current user yet.
460 */
461 private void injectLocation(Location location, int attemptCount) {
Gregory Clark55620c12019-06-03 17:42:58 -0700462 boolean success = false;
463 synchronized (mLocationManagerProxyLock) {
464 if (mILocationManagerProxy == null) {
465 logd("Null location manager proxy.");
466 } else {
467 try {
468 success = mILocationManagerProxy.injectLocation(location);
469 } catch (RemoteException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800470 Slog.e(TAG, "RemoteException from ILocationManagerProxy", e);
Gregory Clark55620c12019-06-03 17:42:58 -0700471 }
472 }
473 }
Gregory Clarka63ba022018-06-07 16:42:12 -0700474 if (success) {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800475 logd("Successfully injected stored location on attempt %s.", attemptCount);
Gregory Clarka63ba022018-06-07 16:42:12 -0700476 return;
477 } else if (attemptCount <= MAX_LOCATION_INJECTION_ATTEMPTS) {
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800478 logd("Failed to inject stored location on attempt %s.", attemptCount);
Gregory Clarka63ba022018-06-07 16:42:12 -0700479 asyncOperation(() -> {
480 injectLocation(location, attemptCount + 1);
481 }, 200 * attemptCount);
482 } else {
483 logd("No location injected.");
484 }
485 }
486
Gregory Clarka440e812019-02-14 16:05:51 -0800487 private File getLocationCacheFile() {
488 SystemInterface systemInterface = CarLocalServices.getService(SystemInterface.class);
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800489 return new File(systemInterface.getSystemCarDir(), FILENAME);
Gregory Clarka440e812019-02-14 16:05:51 -0800490 }
491
Gregory Clark3f9b56d2018-01-31 13:02:41 -0800492 @VisibleForTesting
493 void asyncOperation(Runnable operation) {
Gregory Clarka63ba022018-06-07 16:42:12 -0700494 asyncOperation(operation, 0);
495 }
496
497 private void asyncOperation(Runnable operation, long delayMillis) {
Keun young Parkb241d022020-04-20 20:31:34 -0700498 mHandler.postDelayed(() -> operation.run(), delayMillis);
Gregory Clarkd8136062017-12-11 14:27:53 -0800499 }
500
Gregory Clarkc960e5a2020-01-24 20:45:24 -0800501 private static void logd(String msg, Object... vals) {
502 // Disable logs here if they become too spammy.
Eric Jeongbd5fb562020-12-21 13:49:40 -0800503 Slog.d(TAG, String.format(msg, vals));
Gregory Clarkd8136062017-12-11 14:27:53 -0800504 }
Gregory Clarkd8136062017-12-11 14:27:53 -0800505}