Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package com.android.car; |
| 18 | |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 19 | import android.app.ActivityManager; |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 20 | import android.car.ILocationManagerProxy; |
Eric Jeong | a84a682 | 2019-09-17 15:46:38 -0700 | [diff] [blame] | 21 | import android.car.IPerUserCarService; |
Gregory Clark | 18a0ef9 | 2019-05-23 20:00:49 -0700 | [diff] [blame] | 22 | import android.car.drivingstate.CarDrivingStateEvent; |
| 23 | import android.car.drivingstate.ICarDrivingStateChangeListener; |
Serik Beketayev | 6892054 | 2018-09-06 13:29:58 -0700 | [diff] [blame] | 24 | import android.car.hardware.power.CarPowerManager; |
| 25 | import android.car.hardware.power.CarPowerManager.CarPowerStateListener; |
Jim Kaye | e513316 | 2019-04-22 12:50:27 -0700 | [diff] [blame] | 26 | import android.car.hardware.power.CarPowerManager.CarPowerStateListenerWithCompletion; |
Gregory Clark | e70eb5b | 2018-02-07 18:29:24 -0800 | [diff] [blame] | 27 | import android.content.BroadcastReceiver; |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 28 | import android.content.Context; |
Gregory Clark | e70eb5b | 2018-02-07 18:29:24 -0800 | [diff] [blame] | 29 | import android.content.Intent; |
| 30 | import android.content.IntentFilter; |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 31 | import android.location.Location; |
| 32 | import android.location.LocationManager; |
Gregory Clark | 3f9b56d | 2018-01-31 13:02:41 -0800 | [diff] [blame] | 33 | import android.os.Handler; |
| 34 | import android.os.HandlerThread; |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 35 | import android.os.RemoteException; |
Gregory Clark | 756ed81 | 2018-02-06 18:19:27 -0800 | [diff] [blame] | 36 | import android.os.SystemClock; |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 37 | import android.os.UserHandle; |
Anthony Hugh | 5f2465e | 2019-10-08 15:07:01 -0700 | [diff] [blame] | 38 | import android.os.UserManager; |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 39 | import android.util.AtomicFile; |
Felipe Leme | 176a5fd | 2021-01-20 15:48:33 -0800 | [diff] [blame] | 40 | import android.util.IndentingPrintWriter; |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 41 | import android.util.JsonReader; |
| 42 | import android.util.JsonWriter; |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 43 | import android.util.Slog; |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 44 | |
Gregory Clark | a440e81 | 2019-02-14 16:05:51 -0800 | [diff] [blame] | 45 | import com.android.car.systeminterface.SystemInterface; |
Gregory Clark | 3f9b56d | 2018-01-31 13:02:41 -0800 | [diff] [blame] | 46 | import com.android.internal.annotations.VisibleForTesting; |
| 47 | |
Gregory Clark | a440e81 | 2019-02-14 16:05:51 -0800 | [diff] [blame] | 48 | import java.io.File; |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 49 | import java.io.FileInputStream; |
Gregory Clark | 3f9b56d | 2018-01-31 13:02:41 -0800 | [diff] [blame] | 50 | import java.io.FileNotFoundException; |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 51 | import java.io.FileOutputStream; |
| 52 | import java.io.IOException; |
| 53 | import java.io.InputStreamReader; |
| 54 | import java.io.OutputStreamWriter; |
Serik Beketayev | 6892054 | 2018-09-06 13:29:58 -0700 | [diff] [blame] | 55 | import java.util.concurrent.CompletableFuture; |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 56 | |
| 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 Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 60 | */ |
Gregory Clark | 18a0ef9 | 2019-05-23 20:00:49 -0700 | [diff] [blame] | 61 | public class CarLocationService extends BroadcastReceiver implements CarServiceBase, |
| 62 | CarPowerStateListenerWithCompletion { |
Mayank Garg | 72c71d2 | 2021-02-03 23:54:45 -0800 | [diff] [blame] | 63 | private static final String TAG = CarLog.tagFor(CarLocationService.class); |
Gregory Clark | 67259c2 | 2018-03-09 15:30:21 -0800 | [diff] [blame] | 64 | private static final String FILENAME = "location_cache.json"; |
Gregory Clark | 67259c2 | 2018-03-09 15:30:21 -0800 | [diff] [blame] | 65 | // 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 Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 69 | // The maximum number of times to try injecting a location |
| 70 | private static final int MAX_LOCATION_INJECTION_ATTEMPTS = 10; |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 71 | |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 72 | // 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 Clark | 3f9b56d | 2018-01-31 13:02:41 -0800 | [diff] [blame] | 86 | // Used internally for mHandlerThread synchronization |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 87 | private final Object mLock = new Object(); |
| 88 | |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 89 | // Used internally for mILocationManagerProxy synchronization |
| 90 | private final Object mLocationManagerProxyLock = new Object(); |
| 91 | |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 92 | private final Context mContext; |
Keun young Park | b241d02 | 2020-04-20 20:31:34 -0700 | [diff] [blame] | 93 | private final HandlerThread mHandlerThread = CarServiceUtils.getHandlerThread( |
| 94 | getClass().getSimpleName()); |
| 95 | private final Handler mHandler = new Handler(mHandlerThread.getLooper()); |
| 96 | |
Serik Beketayev | 6892054 | 2018-09-06 13:29:58 -0700 | [diff] [blame] | 97 | private CarPowerManager mCarPowerManager; |
Gregory Clark | 18a0ef9 | 2019-05-23 20:00:49 -0700 | [diff] [blame] | 98 | private CarDrivingStateService mCarDrivingStateService; |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 99 | 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 Jeong | a84a682 | 2019-09-17 15:46:38 -0700 | [diff] [blame] | 108 | public void onServiceConnected(IPerUserCarService perUserCarService) { |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 109 | logd("Connected to PerUserCarService"); |
Eric Jeong | a84a682 | 2019-09-17 15:46:38 -0700 | [diff] [blame] | 110 | if (perUserCarService == null) { |
| 111 | logd("IPerUserCarService is null. Cannot get location manager proxy"); |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 112 | return; |
| 113 | } |
| 114 | synchronized (mLocationManagerProxyLock) { |
| 115 | try { |
Eric Jeong | a84a682 | 2019-09-17 15:46:38 -0700 | [diff] [blame] | 116 | mILocationManagerProxy = perUserCarService.getLocationManagerProxy(); |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 117 | } catch (RemoteException e) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 118 | Slog.e(TAG, "RemoteException from IPerUserCarService", e); |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 119 | return; |
| 120 | } |
| 121 | } |
| 122 | int currentUser = ActivityManager.getCurrentUser(); |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 123 | logd("Current user: %s", currentUser); |
Anthony Hugh | 5f2465e | 2019-10-08 15:07:01 -0700 | [diff] [blame] | 124 | if (UserManager.isHeadlessSystemUserMode() |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 125 | && currentUser > UserHandle.USER_SYSTEM) { |
| 126 | asyncOperation(() -> loadLocation()); |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | @Override |
| 131 | public void onPreUnbind() { |
Gregory Clark | cebacef | 2019-07-09 16:27:36 -0700 | [diff] [blame] | 132 | logd("Before Unbinding from PerUserCarService"); |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 133 | 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 Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 151 | logd("onDrivingStateChanged: %s", event); |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 152 | 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 Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 162 | |
Anthony Hugh | 5f2465e | 2019-10-08 15:07:01 -0700 | [diff] [blame] | 163 | public CarLocationService(Context context) { |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 164 | logd("constructed"); |
| 165 | mContext = context; |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | @Override |
| 169 | public void init() { |
| 170 | logd("init"); |
Gregory Clark | e70eb5b | 2018-02-07 18:29:24 -0800 | [diff] [blame] | 171 | IntentFilter filter = new IntentFilter(); |
Gregory Clark | 29b7f0f | 2018-02-16 17:53:40 -0800 | [diff] [blame] | 172 | filter.addAction(LocationManager.MODE_CHANGED_ACTION); |
Gregory Clark | e70eb5b | 2018-02-07 18:29:24 -0800 | [diff] [blame] | 173 | mContext.registerReceiver(this, filter); |
Gregory Clark | 18a0ef9 | 2019-05-23 20:00:49 -0700 | [diff] [blame] | 174 | 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 Park | a5fa478 | 2019-04-16 18:56:27 -0700 | [diff] [blame] | 184 | mCarPowerManager = CarLocalServices.createCarPowerManager(mContext); |
| 185 | if (mCarPowerManager != null) { // null case happens for testing. |
Jim Kaye | e513316 | 2019-04-22 12:50:27 -0700 | [diff] [blame] | 186 | mCarPowerManager.setListenerWithCompletion(CarLocationService.this); |
Keun young Park | a5fa478 | 2019-04-16 18:56:27 -0700 | [diff] [blame] | 187 | } |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 188 | mPerUserCarServiceHelper = CarLocalServices.getService(PerUserCarServiceHelper.class); |
| 189 | if (mPerUserCarServiceHelper != null) { |
| 190 | mPerUserCarServiceHelper.registerServiceCallback(mUserServiceCallback); |
| 191 | } |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 192 | } |
| 193 | |
| 194 | @Override |
| 195 | public void release() { |
| 196 | logd("release"); |
Keun young Park | a5fa478 | 2019-04-16 18:56:27 -0700 | [diff] [blame] | 197 | if (mCarPowerManager != null) { |
| 198 | mCarPowerManager.clearListener(); |
| 199 | } |
Gregory Clark | 18a0ef9 | 2019-05-23 20:00:49 -0700 | [diff] [blame] | 200 | if (mCarDrivingStateService != null) { |
| 201 | mCarDrivingStateService.unregisterDrivingStateChangeListener( |
| 202 | mICarDrivingStateChangeEventListener); |
| 203 | } |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 204 | if (mPerUserCarServiceHelper != null) { |
| 205 | mPerUserCarServiceHelper.unregisterServiceCallback(mUserServiceCallback); |
| 206 | } |
Gregory Clark | e70eb5b | 2018-02-07 18:29:24 -0800 | [diff] [blame] | 207 | mContext.unregisterReceiver(this); |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 208 | } |
| 209 | |
| 210 | @Override |
Felipe Leme | 176a5fd | 2021-01-20 15:48:33 -0800 | [diff] [blame] | 211 | public void dump(IndentingPrintWriter writer) { |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 212 | writer.println(TAG); |
Felipe Leme | 176a5fd | 2021-01-20 15:48:33 -0800 | [diff] [blame] | 213 | mPerUserCarServiceHelper.dump(writer); |
| 214 | writer.printf("Context: %s\n", mContext); |
| 215 | writer.printf("MAX_LOCATION_INJECTION_ATTEMPTS: %d\n", MAX_LOCATION_INJECTION_ATTEMPTS); |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 216 | } |
| 217 | |
| 218 | @Override |
Serik Beketayev | 6892054 | 2018-09-06 13:29:58 -0700 | [diff] [blame] | 219 | public void onStateChanged(int state, CompletableFuture<Void> future) { |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 220 | logd("onStateChanged: %s", state); |
Serik Beketayev | 6892054 | 2018-09-06 13:29:58 -0700 | [diff] [blame] | 221 | switch (state) { |
Steve Paik | 07db5ed | 2018-09-24 16:48:52 -0700 | [diff] [blame] | 222 | case CarPowerStateListener.SHUTDOWN_PREPARE: |
Gregory Clark | 5b9a9cd | 2018-09-10 13:21:22 -0700 | [diff] [blame] | 223 | asyncOperation(() -> { |
| 224 | storeLocation(); |
| 225 | // Notify the CarPowerManager that it may proceed to shutdown or suspend. |
Gregory Clark | 71ed672 | 2018-09-24 13:10:16 -0700 | [diff] [blame] | 226 | if (future != null) { |
| 227 | future.complete(null); |
| 228 | } |
Gregory Clark | 5b9a9cd | 2018-09-10 13:21:22 -0700 | [diff] [blame] | 229 | }); |
Serik Beketayev | 6892054 | 2018-09-06 13:29:58 -0700 | [diff] [blame] | 230 | break; |
Gregory Clark | 63a4ad2 | 2019-05-23 18:36:33 -0700 | [diff] [blame] | 231 | case CarPowerStateListener.SUSPEND_EXIT: |
Gregory Clark | dfb084f | 2019-07-15 16:41:57 -0700 | [diff] [blame] | 232 | 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 Clark | 63a4ad2 | 2019-05-23 18:36:33 -0700 | [diff] [blame] | 243 | if (future != null) { |
| 244 | future.complete(null); |
| 245 | } |
Steve Paik | 07db5ed | 2018-09-24 16:48:52 -0700 | [diff] [blame] | 246 | default: |
Gregory Clark | 5b9a9cd | 2018-09-10 13:21:22 -0700 | [diff] [blame] | 247 | // This service does not need to do any work for these events but should still |
| 248 | // notify the CarPowerManager that it may proceed. |
Gregory Clark | 71ed672 | 2018-09-24 13:10:16 -0700 | [diff] [blame] | 249 | if (future != null) { |
| 250 | future.complete(null); |
| 251 | } |
Serik Beketayev | 6892054 | 2018-09-06 13:29:58 -0700 | [diff] [blame] | 252 | break; |
| 253 | } |
Gregory Clark | 26fa601 | 2018-03-14 18:38:56 -0700 | [diff] [blame] | 254 | } |
| 255 | |
Gregory Clark | 26fa601 | 2018-03-14 18:38:56 -0700 | [diff] [blame] | 256 | @Override |
Gregory Clark | e70eb5b | 2018-02-07 18:29:24 -0800 | [diff] [blame] | 257 | public void onReceive(Context context, Intent intent) { |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 258 | logd("onReceive %s", intent); |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 259 | // 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 Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 269 | } |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 270 | String action = intent.getAction(); |
| 271 | try { |
| 272 | if (action == LocationManager.MODE_CHANGED_ACTION) { |
| 273 | boolean locationEnabled = mILocationManagerProxy.isLocationEnabled(); |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 274 | logd("isLocationEnabled(): %s", locationEnabled); |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 275 | if (!locationEnabled) { |
| 276 | deleteCacheFile(); |
| 277 | } |
| 278 | } else { |
| 279 | logd("Unexpected intent."); |
| 280 | } |
| 281 | } catch (RemoteException e) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 282 | Slog.e(TAG, "RemoteException from ILocationManagerProxy", e); |
Gregory Clark | 29b7f0f | 2018-02-16 17:53:40 -0800 | [diff] [blame] | 283 | } |
| 284 | } |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 285 | } |
| 286 | |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 287 | /** Tells whether the current foreground user is the headless system user. */ |
| 288 | private boolean isCurrentUserHeadlessSystemUser() { |
| 289 | int currentUserId = ActivityManager.getCurrentUser(); |
Anthony Hugh | 5f2465e | 2019-10-08 15:07:01 -0700 | [diff] [blame] | 290 | return UserManager.isHeadlessSystemUserMode() |
Anthony Hugh | 9932a25 | 2019-06-12 16:19:56 -0700 | [diff] [blame] | 291 | && currentUserId == UserHandle.USER_SYSTEM; |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 292 | } |
| 293 | |
| 294 | /** |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 295 | * Gets the last known location from the location manager proxy and store it in a file. |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 296 | */ |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 297 | private void storeLocation() { |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 298 | 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 Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 308 | Slog.e(TAG, "RemoteException from ILocationManagerProxy", e); |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 309 | } |
| 310 | } |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 311 | if (location == null) { |
| 312 | logd("Not storing null location"); |
| 313 | } else { |
Pierre Fite-Georgel | b189e2b | 2019-10-31 16:50:04 -0700 | [diff] [blame] | 314 | logd("Storing location"); |
Gregory Clark | a440e81 | 2019-02-14 16:05:51 -0800 | [diff] [blame] | 315 | AtomicFile atomicFile = new AtomicFile(getLocationCacheFile()); |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 316 | FileOutputStream fos = null; |
| 317 | try { |
Gregory Clark | 3f9b56d | 2018-01-31 13:02:41 -0800 | [diff] [blame] | 318 | fos = atomicFile.startWrite(); |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 319 | try (JsonWriter jsonWriter = new JsonWriter(new OutputStreamWriter(fos, "UTF-8"))) { |
| 320 | jsonWriter.beginObject(); |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 321 | jsonWriter.name(PROVIDER).value(location.getProvider()); |
| 322 | jsonWriter.name(LATITUDE).value(location.getLatitude()); |
| 323 | jsonWriter.name(LONGITUDE).value(location.getLongitude()); |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 324 | if (location.hasAltitude()) { |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 325 | jsonWriter.name(ALTITUDE).value(location.getAltitude()); |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 326 | } |
| 327 | if (location.hasSpeed()) { |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 328 | jsonWriter.name(SPEED).value(location.getSpeed()); |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 329 | } |
| 330 | if (location.hasBearing()) { |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 331 | jsonWriter.name(BEARING).value(location.getBearing()); |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 332 | } |
| 333 | if (location.hasAccuracy()) { |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 334 | jsonWriter.name(ACCURACY).value(location.getAccuracy()); |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 335 | } |
| 336 | if (location.hasVerticalAccuracy()) { |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 337 | jsonWriter.name(VERTICAL_ACCURACY).value( |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 338 | location.getVerticalAccuracyMeters()); |
| 339 | } |
| 340 | if (location.hasSpeedAccuracy()) { |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 341 | jsonWriter.name(SPEED_ACCURACY).value( |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 342 | location.getSpeedAccuracyMetersPerSecond()); |
| 343 | } |
| 344 | if (location.hasBearingAccuracy()) { |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 345 | jsonWriter.name(BEARING_ACCURACY).value( |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 346 | location.getBearingAccuracyDegrees()); |
| 347 | } |
| 348 | if (location.isFromMockProvider()) { |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 349 | jsonWriter.name(IS_FROM_MOCK_PROVIDER).value(true); |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 350 | } |
| 351 | long currentTime = location.getTime(); |
| 352 | // Round the time down to only be accurate within one day. |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 353 | jsonWriter.name(CAPTURE_TIME).value( |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 354 | currentTime - currentTime % GRANULARITY_ONE_DAY_MS); |
| 355 | jsonWriter.endObject(); |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 356 | } |
Gregory Clark | 3f9b56d | 2018-01-31 13:02:41 -0800 | [diff] [blame] | 357 | atomicFile.finishWrite(fos); |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 358 | } catch (IOException e) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 359 | Slog.e(TAG, "Unable to write to disk", e); |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 360 | atomicFile.failWrite(fos); |
| 361 | } |
| 362 | } |
| 363 | } |
| 364 | |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 365 | /** |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 366 | * Reads a previously stored location and attempts to inject it into the location manager proxy. |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 367 | */ |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 368 | private void loadLocation() { |
Gregory Clark | 67259c2 | 2018-03-09 15:30:21 -0800 | [diff] [blame] | 369 | Location location = readLocationFromCacheFile(); |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 370 | logd("Read location from timestamp %s", location.getTime()); |
Gregory Clark | 67259c2 | 2018-03-09 15:30:21 -0800 | [diff] [blame] | 371 | long currentTime = System.currentTimeMillis(); |
| 372 | if (location.getTime() + TTL_THIRTY_DAYS_MS < currentTime) { |
| 373 | logd("Location expired."); |
Gregory Clark | 63a4ad2 | 2019-05-23 18:36:33 -0700 | [diff] [blame] | 374 | deleteCacheFile(); |
Gregory Clark | 67259c2 | 2018-03-09 15:30:21 -0800 | [diff] [blame] | 375 | } else { |
| 376 | location.setTime(currentTime); |
| 377 | long elapsedTime = SystemClock.elapsedRealtimeNanos(); |
| 378 | location.setElapsedRealtimeNanos(elapsedTime); |
| 379 | if (location.isComplete()) { |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 380 | injectLocation(location, 1); |
Gregory Clark | 67259c2 | 2018-03-09 15:30:21 -0800 | [diff] [blame] | 381 | } |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | private Location readLocationFromCacheFile() { |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 386 | Location location = new Location((String) null); |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 387 | File file = getLocationCacheFile(); |
| 388 | AtomicFile atomicFile = new AtomicFile(file); |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 389 | try (FileInputStream fis = atomicFile.openRead()) { |
Gregory Clark | 3f9b56d | 2018-01-31 13:02:41 -0800 | [diff] [blame] | 390 | JsonReader reader = new JsonReader(new InputStreamReader(fis, "UTF-8")); |
| 391 | reader.beginObject(); |
| 392 | while (reader.hasNext()) { |
| 393 | String name = reader.nextName(); |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 394 | 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 Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 432 | Slog.w(TAG, "Unrecognized key: " + name); |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 433 | reader.skipValue(); |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 434 | } |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 435 | } |
Gregory Clark | 3f9b56d | 2018-01-31 13:02:41 -0800 | [diff] [blame] | 436 | reader.endObject(); |
Gregory Clark | 3f9b56d | 2018-01-31 13:02:41 -0800 | [diff] [blame] | 437 | } catch (FileNotFoundException e) { |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 438 | logd("Location cache file not found: %s", file); |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 439 | } catch (IOException e) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 440 | Slog.e(TAG, "Unable to read from disk", e); |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 441 | } catch (NumberFormatException | IllegalStateException e) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 442 | Slog.e(TAG, "Unexpected format", e); |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 443 | } |
Gregory Clark | 67259c2 | 2018-03-09 15:30:21 -0800 | [diff] [blame] | 444 | return location; |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 445 | } |
| 446 | |
Gregory Clark | 29b7f0f | 2018-02-16 17:53:40 -0800 | [diff] [blame] | 447 | private void deleteCacheFile() { |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 448 | 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 Clark | 29b7f0f | 2018-02-16 17:53:40 -0800 | [diff] [blame] | 455 | } |
| 456 | |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 457 | /** |
| 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 Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 462 | 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 Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 470 | Slog.e(TAG, "RemoteException from ILocationManagerProxy", e); |
Gregory Clark | 55620c1 | 2019-06-03 17:42:58 -0700 | [diff] [blame] | 471 | } |
| 472 | } |
| 473 | } |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 474 | if (success) { |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 475 | logd("Successfully injected stored location on attempt %s.", attemptCount); |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 476 | return; |
| 477 | } else if (attemptCount <= MAX_LOCATION_INJECTION_ATTEMPTS) { |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 478 | logd("Failed to inject stored location on attempt %s.", attemptCount); |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 479 | asyncOperation(() -> { |
| 480 | injectLocation(location, attemptCount + 1); |
| 481 | }, 200 * attemptCount); |
| 482 | } else { |
| 483 | logd("No location injected."); |
| 484 | } |
| 485 | } |
| 486 | |
Gregory Clark | a440e81 | 2019-02-14 16:05:51 -0800 | [diff] [blame] | 487 | private File getLocationCacheFile() { |
| 488 | SystemInterface systemInterface = CarLocalServices.getService(SystemInterface.class); |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 489 | return new File(systemInterface.getSystemCarDir(), FILENAME); |
Gregory Clark | a440e81 | 2019-02-14 16:05:51 -0800 | [diff] [blame] | 490 | } |
| 491 | |
Gregory Clark | 3f9b56d | 2018-01-31 13:02:41 -0800 | [diff] [blame] | 492 | @VisibleForTesting |
| 493 | void asyncOperation(Runnable operation) { |
Gregory Clark | a63ba02 | 2018-06-07 16:42:12 -0700 | [diff] [blame] | 494 | asyncOperation(operation, 0); |
| 495 | } |
| 496 | |
| 497 | private void asyncOperation(Runnable operation, long delayMillis) { |
Keun young Park | b241d02 | 2020-04-20 20:31:34 -0700 | [diff] [blame] | 498 | mHandler.postDelayed(() -> operation.run(), delayMillis); |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 499 | } |
| 500 | |
Gregory Clark | c960e5a | 2020-01-24 20:45:24 -0800 | [diff] [blame] | 501 | private static void logd(String msg, Object... vals) { |
| 502 | // Disable logs here if they become too spammy. |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 503 | Slog.d(TAG, String.format(msg, vals)); |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 504 | } |
Gregory Clark | d813606 | 2017-12-11 14:27:53 -0800 | [diff] [blame] | 505 | } |