• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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