1 /* 2 * Copyright (C) 2022 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 android.car.remoteaccess; 18 19 import static com.android.car.internal.util.VersionUtils.assertPlatformVersionAtLeastU; 20 21 import android.annotation.CallbackExecutor; 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.RequiresPermission; 26 import android.annotation.SystemApi; 27 import android.car.Car; 28 import android.car.CarManagerBase; 29 import android.car.annotation.ApiRequirements; 30 import android.car.annotation.ApiRequirements.CarVersion; 31 import android.car.annotation.ApiRequirements.PlatformVersion; 32 import android.car.builtin.util.Slogf; 33 import android.os.IBinder; 34 import android.os.RemoteException; 35 36 import com.android.internal.annotations.GuardedBy; 37 import com.android.internal.util.Preconditions; 38 39 import java.lang.annotation.ElementType; 40 import java.lang.annotation.Retention; 41 import java.lang.annotation.RetentionPolicy; 42 import java.lang.annotation.Target; 43 import java.util.concurrent.Executor; 44 45 /** 46 * CarRemoteAccessManager allows applications to listen to remote task requests even while Android 47 * System is not running. 48 * 49 * <p>The remote task client registers to {@link CarRemoteAccessManager} to listen to remote access 50 * events. At {@link RemoteTaskClientCallback#onClientRegistered} it is required to share 51 * {@code serviceId}, {@code deviceId} and {@code clientId} with the cloud service which will use 52 * the IDs to wake the vehicle. At {@link RemoteTaskClientCallback#onRemoteTaskRequested}, it starts 53 * executing the given task. It is supposed to call {@link #reportRemoteTaskDone(String)} when it 54 * finishes the given task. Once the task completion is reported or the timeout expires, Android 55 * System goes back to either the previous power state or the specified power state. 56 */ 57 public final class CarRemoteAccessManager extends CarManagerBase { 58 59 private static final String TAG = CarRemoteAccessManager.class.getSimpleName(); 60 61 /** 62 * The system remains ON after completing the remote tasks. 63 * 64 * @hide 65 */ 66 @SystemApi 67 @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_0, 68 minPlatformVersion = PlatformVersion.UPSIDE_DOWN_CAKE_0) 69 public static final int NEXT_POWER_STATE_ON = 1; 70 71 /** 72 * The system shuts down to power off after completing the remote tasks. 73 * 74 * @hide 75 */ 76 @SystemApi 77 @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_0, 78 minPlatformVersion = PlatformVersion.UPSIDE_DOWN_CAKE_0) 79 public static final int NEXT_POWER_STATE_OFF = 2; 80 81 /** 82 * The system goes into deep sleep after completing the remote tasks. 83 * 84 * @hide 85 */ 86 @SystemApi 87 @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_0, 88 minPlatformVersion = PlatformVersion.UPSIDE_DOWN_CAKE_0) 89 public static final int NEXT_POWER_STATE_SUSPEND_TO_RAM = 3; 90 91 /** 92 * The system goes into hibernation after completing the remote tasks. 93 * 94 * @hide 95 */ 96 @SystemApi 97 @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_0, 98 minPlatformVersion = PlatformVersion.UPSIDE_DOWN_CAKE_0) 99 public static final int NEXT_POWER_STATE_SUSPEND_TO_DISK = 4; 100 101 /** @hide */ 102 @Retention(RetentionPolicy.SOURCE) 103 @IntDef(prefix = "NEXT_POWER_STATE_", value = { 104 NEXT_POWER_STATE_ON, 105 NEXT_POWER_STATE_OFF, 106 NEXT_POWER_STATE_SUSPEND_TO_RAM, 107 NEXT_POWER_STATE_SUSPEND_TO_DISK, 108 }) 109 @Target({ElementType.TYPE_USE}) 110 public @interface NextPowerState {} 111 112 private final ICarRemoteAccessService mService; 113 private final Object mLock = new Object(); 114 115 private final ICarRemoteAccessCallback mCarRemoteAccessCallback = 116 new ICarRemoteAccessCallback.Stub() { 117 @Override 118 public void onClientRegistrationUpdated(RemoteTaskClientRegistrationInfo registrationInfo) { 119 RemoteTaskClientCallback callback; 120 Executor executor; 121 synchronized (mLock) { 122 if (mRemoteTaskClientCallback == null || mExecutor == null) { 123 Slogf.w(TAG, "Cannot call onRegistrationUpdated because no remote task client " 124 + "is registered"); 125 return; 126 } 127 mCurrentClientId = registrationInfo.getClientId(); 128 callback = mRemoteTaskClientCallback; 129 executor = mExecutor; 130 } 131 executor.execute(() -> callback.onRegistrationUpdated(registrationInfo)); 132 } 133 134 @Override 135 public void onClientRegistrationFailed() { 136 RemoteTaskClientCallback callback; 137 Executor executor; 138 synchronized (mLock) { 139 if (mRemoteTaskClientCallback == null || mExecutor == null) { 140 Slogf.w(TAG, "Cannot call onRegistrationFailed because no remote task client " 141 + "is registered"); 142 return; 143 } 144 callback = mRemoteTaskClientCallback; 145 executor = mExecutor; 146 } 147 executor.execute(() -> callback.onRegistrationFailed()); 148 } 149 150 @Override 151 public void onRemoteTaskRequested(String clientId, String taskId, byte[] data, 152 int taskMaxDurationInSec) { 153 RemoteTaskClientCallback callback; 154 Executor executor; 155 synchronized (mLock) { 156 if (mCurrentClientId == null || !mCurrentClientId.equals(clientId)) { 157 Slogf.w(TAG, "Received a task for a mismatched client ID(%s): the current " 158 + "client ID = %s", clientId, mCurrentClientId); 159 return; 160 } 161 callback = mRemoteTaskClientCallback; 162 executor = mExecutor; 163 } 164 if (callback == null || executor == null) { 165 Slogf.w(TAG, "Cannot call onRemoteTaskRequested because no remote task client is " 166 + "registered"); 167 return; 168 } 169 executor.execute(() -> callback.onRemoteTaskRequested(taskId, data, 170 taskMaxDurationInSec)); 171 } 172 173 @Override 174 public void onShutdownStarting() { 175 String clientId; 176 RemoteTaskClientCallback callback; 177 Executor executor; 178 synchronized (mLock) { 179 clientId = mCurrentClientId; 180 callback = mRemoteTaskClientCallback; 181 executor = mExecutor; 182 } 183 if (clientId == null || callback == null || executor == null) { 184 Slogf.w(TAG, "Cannot call onShutdownStarting because no remote task client is " 185 + "registered"); 186 return; 187 } 188 executor.execute(() -> 189 callback.onShutdownStarting(new MyCompletableRemoteTaskFuture(clientId))); 190 } 191 }; 192 193 @GuardedBy("mLock") 194 private RemoteTaskClientCallback mRemoteTaskClientCallback; 195 @GuardedBy("mLock") 196 private Executor mExecutor; 197 @GuardedBy("mLock") 198 private String mCurrentClientId; 199 200 /** 201 * An interface passed from {@link RemoteTaskClientCallback}. 202 * 203 * <p>The remote task client uses this interface to tell {@link CarRemoteAccessManager} that it 204 * finalized the pending remote tasks. 205 */ 206 public interface CompletableRemoteTaskFuture { 207 /** 208 * Tells {@link CarRemoteAccessManager} that the remote task client finalized the pending 209 * remoate tasks. 210 */ 211 @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_0, 212 minPlatformVersion = PlatformVersion.UPSIDE_DOWN_CAKE_0) complete()213 void complete(); 214 } 215 216 private final class MyCompletableRemoteTaskFuture implements CompletableRemoteTaskFuture { 217 private final String mClientIdToComplete; 218 MyCompletableRemoteTaskFuture(String clientId)219 MyCompletableRemoteTaskFuture(String clientId) { 220 mClientIdToComplete = clientId; 221 } 222 223 @Override complete()224 public void complete() { 225 try { 226 mService.confirmReadyForShutdown(mClientIdToComplete); 227 } catch (RemoteException e) { 228 handleRemoteExceptionFromCarService(e); 229 } 230 } 231 } 232 233 /** 234 * Listener for remote task events. 235 */ 236 public interface RemoteTaskClientCallback { 237 /** 238 * This is called when the remote task client is successfully registered or the client ID is 239 * updated by AAOS. 240 * 241 * @param info {@link RemoteTaskClientRegistrationIfno} which contains wake-up service ID, 242 * vehicle ID, processor ID and client ID. 243 */ 244 @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_0, 245 minPlatformVersion = PlatformVersion.UPSIDE_DOWN_CAKE_0) onRegistrationUpdated(@onNull RemoteTaskClientRegistrationInfo info)246 void onRegistrationUpdated(@NonNull RemoteTaskClientRegistrationInfo info); 247 248 /** 249 * This is called when registering the remote task client fails. 250 */ 251 @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_0, 252 minPlatformVersion = PlatformVersion.UPSIDE_DOWN_CAKE_0) onRegistrationFailed()253 void onRegistrationFailed(); 254 255 /** 256 * This is called when a wake-up request is received/processed. 257 * 258 * @param taskId ID of the task that is requested by the remote task server. 259 * @param data Extra data passed along with the wake-up request. 260 * @param taskMaxDurationInSec The timeout before AAOS goes back to the previous power 261 * state. 262 */ 263 @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_0, 264 minPlatformVersion = PlatformVersion.UPSIDE_DOWN_CAKE_0) onRemoteTaskRequested(@onNull String taskId, @Nullable byte[] data, int taskMaxDurationInSec)265 void onRemoteTaskRequested(@NonNull String taskId, @Nullable byte[] data, 266 int taskMaxDurationInSec); 267 268 /** 269 * This is called when the device is about to shutdown. 270 * 271 * <p>The remote task client should finalize the ongoing tasks, if any, and complete the 272 * given future within 5 seconds. After the given timeout, the Android system will shutdown, 273 * anyway. 274 * 275 * @param future {@link CompletableRemoteTaskFuture} used by the remote task client to 276 * notify CarRemoteAccessManager that all pending remote tasks are finalized. 277 */ 278 @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_0, 279 minPlatformVersion = PlatformVersion.UPSIDE_DOWN_CAKE_0) onShutdownStarting(@onNull CompletableRemoteTaskFuture future)280 void onShutdownStarting(@NonNull CompletableRemoteTaskFuture future); 281 } 282 283 /** @hide */ CarRemoteAccessManager(Car car, IBinder service)284 public CarRemoteAccessManager(Car car, IBinder service) { 285 super(car); 286 mService = ICarRemoteAccessService.Stub.asInterface(service); 287 } 288 289 /** @hide */ 290 @Override 291 @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_0, 292 minPlatformVersion = PlatformVersion.UPSIDE_DOWN_CAKE_0) onCarDisconnected()293 public void onCarDisconnected() { 294 // Nothing to do. 295 } 296 297 /** 298 * Sets the remote task client represented as {@link RemoteTaskClientCallback}. 299 * 300 * @param executor Executor on which {@code callback} is executed. 301 * @param callback {@link RemoteTaskClientCallback} that listens to remote task events. 302 * @throws IllegalStateException When a remote task client is already set. 303 * @throws IllegalArgumentException When the given callback or the executor is {@code null}. 304 */ 305 @RequiresPermission(Car.PERMISSION_USE_REMOTE_ACCESS) 306 @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_0, 307 minPlatformVersion = PlatformVersion.UPSIDE_DOWN_CAKE_0) setRemoteTaskClient(@onNull @allbackExecutor Executor executor, @NonNull RemoteTaskClientCallback callback)308 public void setRemoteTaskClient(@NonNull @CallbackExecutor Executor executor, 309 @NonNull RemoteTaskClientCallback callback) { 310 assertPlatformVersionAtLeastU(); 311 Preconditions.checkArgument(executor != null, "Executor cannot be null"); 312 Preconditions.checkArgument(callback != null, "Callback cannot be null"); 313 314 synchronized (mLock) { 315 if (mRemoteTaskClientCallback != null) { 316 throw new IllegalStateException("Remote task client must be cleared first"); 317 } 318 mRemoteTaskClientCallback = callback; 319 mExecutor = executor; 320 } 321 322 try { 323 mService.addCarRemoteTaskClient(mCarRemoteAccessCallback); 324 } catch (RemoteException e) { 325 synchronized (mLock) { 326 mRemoteTaskClientCallback = null; 327 mExecutor = null; 328 } 329 handleRemoteExceptionFromCarService(e); 330 } 331 } 332 333 /** 334 * Clears the remote task client previously set via {@link #setRemoteTaskClient(Executor, 335 * RemoteTaskClientCallback)}. 336 * 337 * <p>After the remote task client is cleared, all tasks associated with the previous client 338 * will not be delivered and the client must not call {@code reportRemoteTaskDone} with the 339 * task ID associated with the previous client ID. 340 * 341 * @throws IllegalStateException if {@code callback} is not registered. 342 */ 343 @RequiresPermission(Car.PERMISSION_USE_REMOTE_ACCESS) 344 @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_0, 345 minPlatformVersion = PlatformVersion.UPSIDE_DOWN_CAKE_0) clearRemoteTaskClient()346 public void clearRemoteTaskClient() { 347 assertPlatformVersionAtLeastU(); 348 synchronized (mLock) { 349 if (mRemoteTaskClientCallback == null) { 350 Slogf.w(TAG, "No registered remote task client to clear"); 351 return; 352 } 353 mRemoteTaskClientCallback = null; 354 mExecutor = null; 355 mCurrentClientId = null; 356 } 357 try { 358 mService.removeCarRemoteTaskClient(mCarRemoteAccessCallback); 359 } catch (RemoteException e) { 360 handleRemoteExceptionFromCarService(e); 361 } 362 } 363 364 /** 365 * Reports that remote tast execution is completed, so that the vehicle will go back to the 366 * power state before the wake-up. 367 * 368 * @param taskId ID of the remote task which has been completed. 369 * @throws IllegalArgumentException If {@code taskId} is null. 370 * @throws IllegalStateException If the remote task client is not registered or not woken up. 371 */ 372 @RequiresPermission(Car.PERMISSION_USE_REMOTE_ACCESS) 373 @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_0, 374 minPlatformVersion = PlatformVersion.UPSIDE_DOWN_CAKE_0) reportRemoteTaskDone(@onNull String taskId)375 public void reportRemoteTaskDone(@NonNull String taskId) { 376 assertPlatformVersionAtLeastU(); 377 Preconditions.checkArgument(taskId != null, "Task ID cannot be null"); 378 379 String currentClientId; 380 synchronized (mLock) { 381 if (mCurrentClientId == null) { 382 Slogf.w(TAG, "Failed to report remote task completion: no remote task client is " 383 + "registered"); 384 throw new IllegalStateException("No remote task client is registered"); 385 } 386 currentClientId = mCurrentClientId; 387 } 388 try { 389 mService.reportRemoteTaskDone(currentClientId, taskId); 390 } catch (IllegalStateException e) { 391 Slogf.w(TAG, "Task ID(%s) is not valid: %s", taskId, e); 392 throw e; 393 } catch (RemoteException e) { 394 handleRemoteExceptionFromCarService(e); 395 } 396 } 397 398 /** 399 * Sets the power state after all the remote tasks are completed. 400 * 401 * <p>By default, the system returns to the previous power state from which the system woke up. 402 * If the given power state is {@code NEXT_POWER_STATE_ON}, Garage Mode is not executed. 403 * 404 * @param nextPowerState The next power state after the remote task is completed. 405 * @param runGarageMode Whether to run Garage Mode when switching to the next power state. 406 * @throws IllegalArgumentException If {@code nextPowerState} is not valid. 407 * @throws IllegalStateException If the remote task client is not registered or not woken up. 408 * 409 * @hide 410 */ 411 @SystemApi 412 @RequiresPermission(Car.PERMISSION_CONTROL_REMOTE_ACCESS) 413 @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_0, 414 minPlatformVersion = PlatformVersion.UPSIDE_DOWN_CAKE_0) setPowerStatePostTaskExecution(@extPowerState int nextPowerState, boolean runGarageMode)415 public void setPowerStatePostTaskExecution(@NextPowerState int nextPowerState, 416 boolean runGarageMode) { 417 assertPlatformVersionAtLeastU(); 418 try { 419 mService.setPowerStatePostTaskExecution(nextPowerState, runGarageMode); 420 } catch (RemoteException e) { 421 handleRemoteExceptionFromCarService(e); 422 } 423 } 424 } 425