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.app.wearable;
18 
19 import android.Manifest;
20 import android.annotation.CallbackExecutor;
21 import android.annotation.FlaggedApi;
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.annotation.SystemService;
28 import android.app.PendingIntent;
29 import android.app.ambientcontext.AmbientContextEvent;
30 import android.app.compat.CompatChanges;
31 import android.companion.CompanionDeviceManager;
32 import android.compat.annotation.ChangeId;
33 import android.compat.annotation.EnabledSince;
34 import android.content.ComponentName;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.os.Binder;
38 import android.os.Build;
39 import android.os.ParcelFileDescriptor;
40 import android.os.PersistableBundle;
41 import android.os.RemoteCallback;
42 import android.os.RemoteException;
43 import android.os.SharedMemory;
44 import android.service.wearable.WearableSensingService;
45 import android.system.OsConstants;
46 import android.util.Slog;
47 
48 import com.android.internal.infra.AndroidFuture;
49 
50 import java.io.File;
51 import java.io.FileNotFoundException;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.io.OutputStream;
55 import java.lang.annotation.Retention;
56 import java.lang.annotation.RetentionPolicy;
57 import java.util.concurrent.Executor;
58 import java.util.function.Consumer;
59 
60 /**
61  * Allows granted apps to manage the WearableSensingService.
62  * Applications are responsible for managing the connection to Wearables. Applications can choose
63  * to provide a data stream to the WearableSensingService to use for
64  * computing {@link AmbientContextEvent}s. Applications can also optionally provide their own
65  * defined data to power the detection of {@link AmbientContextEvent}s.
66  * Methods on this class requires the caller to hold and be granted the
67  * {@link Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE}.
68  *
69  * <p>The use of "Wearable" here is not the same as the Android Wear platform and should be treated
70  * separately. </p>
71  *
72  * @hide
73  */
74 @SystemApi
75 @SystemService(Context.WEARABLE_SENSING_SERVICE)
76 public class WearableSensingManager {
77     /**
78      * The bundle key for the service status query result, used in
79      * {@code RemoteCallback#sendResult}.
80      *
81      * @hide
82      */
83     public static final String STATUS_RESPONSE_BUNDLE_KEY =
84             "android.app.wearable.WearableSensingStatusBundleKey";
85 
86     /**
87      * The Intent extra key for the data request in the Intent sent to the PendingIntent registered
88      * with {@link #registerDataRequestObserver(int, PendingIntent, Executor, Consumer)}.
89      *
90      * @hide
91      */
92     public static final String EXTRA_WEARABLE_SENSING_DATA_REQUEST =
93             "android.app.wearable.extra.WEARABLE_SENSING_DATA_REQUEST";
94 
95     /**
96      * An unknown status.
97      */
98     public static final int STATUS_UNKNOWN = 0;
99 
100     /**
101      * The value of the status code that indicates success.
102      */
103     public static final int STATUS_SUCCESS = 1;
104 
105     /**
106      * The value of the status code that indicates one or more of the requested events are not
107      * supported.
108      *
109      * @deprecated WearableSensingManager does not deal with events. Use {@link
110      * STATUS_UNSUPPORTED_OPERATION} instead for operations not supported by the implementation of
111      * {@link WearableSensingService}.
112      */
113     @Deprecated
114     public static final int STATUS_UNSUPPORTED = 2;
115 
116     /**
117      * The value of the status code that indicates service not available.
118      */
119     public static final int STATUS_SERVICE_UNAVAILABLE = 3;
120 
121     /**
122      * The value of the status code that there's no connection to the wearable.
123      */
124     public static final int STATUS_WEARABLE_UNAVAILABLE = 4;
125 
126     /**
127      * The value of the status code that the app is not granted access.
128      */
129     public static final int STATUS_ACCESS_DENIED = 5;
130 
131     /**
132      * The value of the status code that indicates the method called is not supported by the
133      * implementation of {@link WearableSensingService}.
134      */
135     @FlaggedApi(Flags.FLAG_ENABLE_UNSUPPORTED_OPERATION_STATUS_CODE)
136     public static final int STATUS_UNSUPPORTED_OPERATION = 6;
137 
138     /**
139      * The value of the status code that indicates an error occurred in the encrypted channel backed
140      * by the provided connection. See {@link #provideConnection(ParcelFileDescriptor,
141      * Executor, Consumer)}.
142      */
143     @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
144     public static final int STATUS_CHANNEL_ERROR = 7;
145 
146     /** The value of the status code that indicates the provided data type is not supported. */
147     @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
148     public static final int STATUS_UNSUPPORTED_DATA_TYPE = 8;
149 
150     /** @hide */
151     @IntDef(
152             prefix = {"STATUS_"},
153             value = {
154                 STATUS_UNKNOWN,
155                 STATUS_SUCCESS,
156                 STATUS_UNSUPPORTED,
157                 STATUS_SERVICE_UNAVAILABLE,
158                 STATUS_WEARABLE_UNAVAILABLE,
159                 STATUS_ACCESS_DENIED,
160                 STATUS_UNSUPPORTED_OPERATION,
161                 STATUS_CHANNEL_ERROR,
162                 STATUS_UNSUPPORTED_DATA_TYPE
163             })
164     @Retention(RetentionPolicy.SOURCE)
165     public @interface StatusCode {}
166 
167     /**
168      * If the WearableSensingService implementation belongs to the same APK as the caller, calling
169      * {@link #provideDataStream(ParcelFileDescriptor, Executor, Consumer)} will allow
170      * WearableSensingService to read from the caller's file directory via {@link
171      * Context#openFileInput(String)}. The read will be proxied via the caller's process and
172      * executed by the {@code executor} provided to this method.
173      */
174     @ChangeId
175     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
176     static final long ALLOW_WEARABLE_SENSING_SERVICE_FILE_READ = 330701114L;
177 
178     /**
179      * Retrieves a {@link WearableSensingDataRequest} from the Intent sent to the PendingIntent
180      * provided to {@link #registerDataRequestObserver(int, PendingIntent, Executor, Consumer)}.
181      *
182      * @param intent The Intent received from the PendingIntent.
183      * @return The WearableSensingDataRequest in the provided Intent, or null if the Intent does not
184      *     contain a WearableSensingDataRequest.
185      */
186     @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
187     @Nullable
getDataRequestFromIntent(@onNull Intent intent)188     public static WearableSensingDataRequest getDataRequestFromIntent(@NonNull Intent intent) {
189         return intent.getParcelableExtra(
190                 EXTRA_WEARABLE_SENSING_DATA_REQUEST, WearableSensingDataRequest.class);
191     }
192 
193     private static final String TAG = WearableSensingManager.class.getSimpleName();
194     private final Context mContext;
195     private final IWearableSensingManager mService;
196 
197     /**
198      * {@hide}
199      */
WearableSensingManager(Context context, IWearableSensingManager service)200     public WearableSensingManager(Context context, IWearableSensingManager service) {
201         mContext = context;
202         mService = service;
203     }
204 
205     /**
206      * Provides a remote wearable device connection to the WearableSensingService and sends the
207      * resulting status to the {@code statusConsumer} after the call.
208      *
209      * <p>This is used by applications that will also provide an implementation of the isolated
210      * WearableSensingService.
211      *
212      * <p>The provided {@code wearableConnection} is expected to be a connection to a remotely
213      * connected wearable device. This {@code wearableConnection} will be attached to
214      * CompanionDeviceManager via {@link CompanionDeviceManager#attachSystemDataTransport(int,
215      * InputStream, OutputStream)}, which will create an encrypted channel using {@code
216      * wearableConnection} as the raw underlying connection. The wearable device is expected to
217      * attach its side of the raw connection to its CompanionDeviceManager via the same method so
218      * that the two CompanionDeviceManagers on the two devices can perform attestation and set up
219      * the encrypted channel. Attestation requirements are listed in
220      * com.android.server.security.AttestationVerificationPeerDeviceVerifier
221      *
222      * <p>A proxy to the encrypted channel will be provided to the WearableSensingService, which is
223      * referred to as the secureWearableConnection in WearableSensingService. Any data written to
224      * secureWearableConnection will be encrypted by CompanionDeviceManager and sent over the raw
225      * {@code wearableConnection} to the remote wearable device, which is expected to use its
226      * CompanionDeviceManager to decrypt the data. Encrypted data arriving at the raw {@code
227      * wearableConnection} will be decrypted by CompanionDeviceManager and be readable as plain text
228      * from secureWearableConnection. The raw {@code wearableConnection} provided to this method
229      * will not be directly available to the WearableSensingService.
230      *
231      * <p>If an error occurred in the encrypted channel (such as the underlying stream closed), the
232      * system will send a status code of {@link STATUS_CHANNEL_ERROR} to the {@code statusConsumer}
233      * and kill the WearableSensingService process.
234      *
235      * <p>Before providing the secureWearableConnection, the system will restart the
236      * WearableSensingService process if it has not been restarted since the last
237      * secureWearableConnection was provided. Other method calls into WearableSensingService may be
238      * dropped during the restart. The caller is responsible for ensuring other method calls are
239      * queued until a success status is returned from the {@code statusConsumer}.
240      *
241      * <p>If the WearableSensingService implementation belongs to the same APK as the caller,
242      * calling this method will allow WearableSensingService to read from the caller's file
243      * directory via {@link Context#openFileInput(String)}. The read will be proxied via the
244      * caller's process and executed by the {@code executor} provided to this method.
245      *
246      * @param wearableConnection The connection to provide
247      * @param executor Executor on which to run the consumer callback
248      * @param statusConsumer A consumer that handles the status codes for providing the connection
249      *     and errors in the encrypted channel.
250      */
251     @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
252     @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
provideConnection( @onNull ParcelFileDescriptor wearableConnection, @NonNull @CallbackExecutor Executor executor, @NonNull @StatusCode Consumer<Integer> statusConsumer)253     public void provideConnection(
254             @NonNull ParcelFileDescriptor wearableConnection,
255             @NonNull @CallbackExecutor Executor executor,
256             @NonNull @StatusCode Consumer<Integer> statusConsumer) {
257         RemoteCallback statusCallback = createStatusCallback(executor, statusConsumer);
258         try {
259             // The wearableSensingCallback is included in this method call even though it is not
260             // semantically related to the connection because we want to avoid race conditions
261             // during the process restart triggered by this method call. See
262             // com.android.server.wearable.RemoteWearableSensingService for details.
263             mService.provideConnection(
264                     wearableConnection, createWearableSensingCallback(executor), statusCallback);
265         } catch (RemoteException e) {
266             throw e.rethrowFromSystemServer();
267         }
268     }
269 
270     /**
271      * Provides a data stream to the WearableSensingService that's backed by the
272      * parcelFileDescriptor, and sends the result to the {@link Consumer} right after the call. This
273      * is used by applications that will also provide an implementation of an isolated
274      * WearableSensingService. If the data stream was provided successfully {@link
275      * WearableSensingManager#STATUS_SUCCESS} will be provided.
276      *
277      * <p>Starting from target SDK level 35, if the WearableSensingService implementation belongs to
278      * the same APK as the caller, calling this method will allow WearableSensingService to read
279      * from the caller's file directory via {@link Context#openFileInput(String)}. The read will be
280      * proxied via the caller's process and executed by the {@code executor} provided to this
281      * method.
282      *
283      * @param parcelFileDescriptor The data stream to provide
284      * @param executor Executor on which to run the consumer callback
285      * @param statusConsumer A consumer that handles the status codes, which is returned right after
286      *     the call.
287      * @deprecated Use {@link #provideConnection(ParcelFileDescriptor, Executor, Consumer)} instead
288      *     to provide a remote wearable device connection to the WearableSensingService
289      */
290     @Deprecated
291     @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
provideDataStream( @onNull ParcelFileDescriptor parcelFileDescriptor, @NonNull @CallbackExecutor Executor executor, @NonNull @StatusCode Consumer<Integer> statusConsumer)292     public void provideDataStream(
293             @NonNull ParcelFileDescriptor parcelFileDescriptor,
294             @NonNull @CallbackExecutor Executor executor,
295             @NonNull @StatusCode Consumer<Integer> statusConsumer) {
296         RemoteCallback statusCallback = createStatusCallback(executor, statusConsumer);
297         IWearableSensingCallback wearableSensingCallback = null;
298         if (CompatChanges.isChangeEnabled(ALLOW_WEARABLE_SENSING_SERVICE_FILE_READ)) {
299             wearableSensingCallback = createWearableSensingCallback(executor);
300         }
301         try {
302             mService.provideDataStream(
303                     parcelFileDescriptor, wearableSensingCallback, statusCallback);
304         } catch (RemoteException e) {
305             throw e.rethrowFromSystemServer();
306         }
307     }
308 
309     /**
310      * Sets configuration and provides read-only data in a {@link PersistableBundle} that may be
311      * used by the WearableSensingService, and sends the result to the {@link Consumer}
312      * right after the call. It is dependent on the application to
313      * define the type of data to provide. This is used by applications that will also
314      * provide an implementation of an isolated WearableSensingService. If the data was
315      * provided successfully {@link WearableSensingManager#STATUS_SUCCESS} will be povided.
316      *
317      * @param data Application configuration data to provide to the {@link WearableSensingService}.
318      *             PersistableBundle does not allow any remotable objects or other contents
319      *             that can be used to communicate with other processes.
320      * @param sharedMemory The unrestricted data blob to
321      *                     provide to the {@link WearableSensingService}. Use this to provide the
322      *                     sensing models data or other such data to the trusted process.
323      *                     The sharedMemory must be read only and protected with
324      *                     {@link OsConstants.PROT_READ}.
325      *                     Other operations will be removed by the system.
326      * @param executor Executor on which to run the consumer callback
327      * @param statusConsumer A consumer that handles the status codes, which is returned
328      *                     right after the call
329      */
330     @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
provideData( @onNull PersistableBundle data, @Nullable SharedMemory sharedMemory, @NonNull @CallbackExecutor Executor executor, @NonNull @StatusCode Consumer<Integer> statusConsumer)331     public void provideData(
332             @NonNull PersistableBundle data, @Nullable SharedMemory sharedMemory,
333             @NonNull @CallbackExecutor Executor executor,
334             @NonNull @StatusCode Consumer<Integer> statusConsumer) {
335         try {
336             RemoteCallback callback = createStatusCallback(executor, statusConsumer);
337             mService.provideData(data, sharedMemory, callback);
338         } catch (RemoteException e) {
339             throw e.rethrowFromSystemServer();
340         }
341     }
342 
343     /**
344      * Registers a data request observer for the provided data type.
345      *
346      * <p>When data is requested, the provided {@code dataRequestPendingIntent} will be invoked. A
347      * {@link WearableSensingDataRequest} can be extracted from the Intent sent to {@code
348      * dataRequestPendingIntent} by calling {@link #getDataRequestFromIntent(Intent)}. The observer
349      * can then provide the requested data via {@link #provideData(PersistableBundle, SharedMemory,
350      * Executor, Consumer)}.
351      *
352      * <p>There is no limit to the number of observers registered for a data type. How they are
353      * handled depends on the implementation of WearableSensingService.
354      *
355      * <p>When the observer is no longer needed, {@link #unregisterDataRequestObserver(int,
356      * PendingIntent, Executor, Consumer)} should be called with the same {@code
357      * dataRequestPendingIntent}. It should be done regardless of the status code returned from
358      * {@code statusConsumer} in order to clean up housekeeping data for the {@code
359      * dataRequestPendingIntent} maintained by the system.
360      *
361      * <p>Example:
362      *
363      * <pre>{@code
364      * // Create a PendingIntent for MyDataRequestBroadcastReceiver
365      * Intent intent =
366      *         new Intent(actionString).setClass(context, MyDataRequestBroadcastReceiver.class);
367      * PendingIntent pendingIntent = PendingIntent.getBroadcast(
368      *         context, 0, intent, PendingIntent.FLAG_MUTABLE);
369      *
370      * // Register the PendingIntent as a data request observer
371      * wearableSensingManager.registerDataRequestObserver(
372      *         dataType, pendingIntent, executor, statusConsumer);
373      *
374      * // Within MyDataRequestBroadcastReceiver, receive the broadcast Intent and extract the
375      * // WearableSensingDataRequest
376      * {@literal @}Override
377      * public void onReceive(Context context, Intent intent) {
378      *     WearableSensingDataRequest dataRequest =
379      *             WearableSensingManager.getDataRequestFromIntent(intent);
380      *     // After parsing the dataRequest, provide the data
381      *     wearableSensingManager.provideData(data, sharedMemory, executor, statusConsumer);
382      * }
383      * }</pre>
384      *
385      * @param dataType The data type to listen to. Values are defined by the application that
386      *     implements {@link WearableSensingService}.
387      * @param dataRequestPendingIntent A mutable {@link PendingIntent} that will be invoked when
388      *     data is requested. See {@link #getDataRequestFromIntent(Intent)}. Activities are not
389      *     allowed to be launched using this PendingIntent.
390      * @param executor Executor on which to run the consumer callback.
391      * @param statusConsumer A consumer that handles the status code for the observer registration.
392      */
393     @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
394     @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
registerDataRequestObserver( int dataType, @NonNull PendingIntent dataRequestPendingIntent, @NonNull @CallbackExecutor Executor executor, @NonNull @StatusCode Consumer<Integer> statusConsumer)395     public void registerDataRequestObserver(
396             int dataType,
397             @NonNull PendingIntent dataRequestPendingIntent,
398             @NonNull @CallbackExecutor Executor executor,
399             @NonNull @StatusCode Consumer<Integer> statusConsumer) {
400         try {
401             RemoteCallback statusCallback = createStatusCallback(executor, statusConsumer);
402             mService.registerDataRequestObserver(
403                     dataType, dataRequestPendingIntent, statusCallback);
404         } catch (RemoteException e) {
405             throw e.rethrowFromSystemServer();
406         }
407     }
408 
409     /**
410      * Unregisters a previously registered data request observer. If the provided {@link
411      * PendingIntent} was not registered, or is already unregistered, the {@link
412      * WearableSensingService} will not be notified.
413      *
414      * @param dataType The data type the observer is for.
415      * @param dataRequestPendingIntent The observer to unregister.
416      * @param executor Executor on which to run the consumer callback.
417      * @param statusConsumer A consumer that handles the status code for the observer
418      *     unregistration.
419      */
420     @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
421     @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
unregisterDataRequestObserver( int dataType, @NonNull PendingIntent dataRequestPendingIntent, @NonNull @CallbackExecutor Executor executor, @NonNull @StatusCode Consumer<Integer> statusConsumer)422     public void unregisterDataRequestObserver(
423             int dataType,
424             @NonNull PendingIntent dataRequestPendingIntent,
425             @NonNull @CallbackExecutor Executor executor,
426             @NonNull @StatusCode Consumer<Integer> statusConsumer) {
427         try {
428             RemoteCallback statusCallback = createStatusCallback(executor, statusConsumer);
429             mService.unregisterDataRequestObserver(
430                     dataType, dataRequestPendingIntent, statusCallback);
431         } catch (RemoteException e) {
432             throw e.rethrowFromSystemServer();
433         }
434     }
435 
436     /**
437      * Requests the wearable to start hotword recognition.
438      *
439      * <p>When this method is called, the system will attempt to provide a {@code
440      * Consumer<android.service.voice.HotwordAudioStream>} to {@link WearableSensingService}. After
441      * first-stage hotword is detected on a wearable, {@link WearableSensingService} should send the
442      * hotword audio to the {@code Consumer<android.service.voice.HotwordAudioStream>}, which will
443      * forward the data to the {@link android.service.voice.HotwordDetectionService} for
444      * second-stage hotword validation. If hotword is detected there, the audio data will be
445      * forwarded to the {@link android.service.voice.VoiceInteractionService}.
446      *
447      * <p>If the {@code targetVisComponentName} provided here is not null, when {@link
448      * WearableSensingService} sends hotword audio to the {@code
449      * Consumer<android.service.voice.HotwordAudioStream>}, the system will check whether the {@link
450      * android.service.voice.VoiceInteractionService} at that time is {@code
451      * targetVisComponentName}. If not, the system will call {@link
452      * WearableSensingService#onStopHotwordAudioStream()} and will not forward the audio
453      * data to the current {@link android.service.voice.HotwordDetectionService} nor {@link
454      * android.service.voice.VoiceInteractionService}. The system will not send a status code to
455      * {@code statusConsumer} regarding the {@code targetVisComponentName} check. The caller is
456      * responsible for determining whether the system's {@link
457      * android.service.voice.VoiceInteractionService} is the same as {@code targetVisComponentName}.
458      * The check here is just a protection against race conditions.
459      *
460      * <p>Calling this method again will send a new {@code
461      * Consumer<android.service.voice.HotwordAudioStream>} to {@link WearableSensingService}. For
462      * audio data sent to the new consumer, the system will perform the above check using the newly
463      * provided {@code targetVisComponentName}. The {@link WearableSensingService} should not
464      * continue to use the previous consumers after receiving a new one.
465      *
466      * <p>If the {@code statusConsumer} returns {@link STATUS_SUCCESS}, the caller should call
467      * {@link #stopHotwordRecognition(Executor, Consumer)} when it wants the wearable to stop
468      * listening for hotword. If the {@code statusConsumer} returns any other status code, a failure
469      * has occurred and calling {@link #stopHotwordRecognition(Executor, Consumer)} is not
470      * required. The system will not retry listening automatically. The caller should call this
471      * method again if they want to retry.
472      *
473      * <p>If a failure occurred after the {@link statusConsumer} returns {@link STATUS_SUCCESS},
474      * {@link statusConsumer} will be invoked again with a status code other than {@link
475      * STATUS_SUCCESS}.
476      *
477      * @param targetVisComponentName The ComponentName of the target VoiceInteractionService.
478      * @param executor Executor on which to run the consumer callback.
479      * @param statusConsumer A consumer that handles the status codes.
480      */
481     @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
482     @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
startHotwordRecognition( @ullable ComponentName targetVisComponentName, @NonNull @CallbackExecutor Executor executor, @NonNull @StatusCode Consumer<Integer> statusConsumer)483     public void startHotwordRecognition(
484             @Nullable ComponentName targetVisComponentName,
485             @NonNull @CallbackExecutor Executor executor,
486             @NonNull @StatusCode Consumer<Integer> statusConsumer) {
487         try {
488             mService.startHotwordRecognition(
489                     targetVisComponentName, createStatusCallback(executor, statusConsumer));
490         } catch (RemoteException e) {
491             throw e.rethrowFromSystemServer();
492         }
493     }
494 
495     /**
496      * Requests the wearable to stop hotword recognition.
497      *
498      * @param executor Executor on which to run the consumer callback.
499      * @param statusConsumer A consumer that handles the status codes.
500      */
501     @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
502     @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
stopHotwordRecognition( @onNull @allbackExecutor Executor executor, @NonNull @StatusCode Consumer<Integer> statusConsumer)503     public void stopHotwordRecognition(
504             @NonNull @CallbackExecutor Executor executor,
505             @NonNull @StatusCode Consumer<Integer> statusConsumer) {
506         try {
507             mService.stopHotwordRecognition(createStatusCallback(executor, statusConsumer));
508         } catch (RemoteException e) {
509             throw e.rethrowFromSystemServer();
510         }
511     }
512 
createStatusCallback( Executor executor, Consumer<Integer> statusConsumer)513     private static RemoteCallback createStatusCallback(
514             Executor executor, Consumer<Integer> statusConsumer) {
515         return new RemoteCallback(
516                 result -> {
517                     int status = result.getInt(STATUS_RESPONSE_BUNDLE_KEY);
518                     final long identity = Binder.clearCallingIdentity();
519                     try {
520                         executor.execute(() -> statusConsumer.accept(status));
521                     } finally {
522                         Binder.restoreCallingIdentity(identity);
523                     }
524                 });
525     }
526 
createWearableSensingCallback(Executor executor)527     private IWearableSensingCallback createWearableSensingCallback(Executor executor) {
528         return new IWearableSensingCallback.Stub() {
529 
530             @Override
531             public void openFile(String filename, AndroidFuture<ParcelFileDescriptor> future) {
532                 Slog.d(TAG, "IWearableSensingCallback#openFile " + filename);
533                 Binder.withCleanCallingIdentity(
534                         () ->
535                                 executor.execute(
536                                         () -> {
537                                             File file = new File(mContext.getFilesDir(), filename);
538                                             ParcelFileDescriptor pfd = null;
539                                             try {
540                                                 pfd =
541                                                         ParcelFileDescriptor.open(
542                                                                 file,
543                                                                 ParcelFileDescriptor
544                                                                         .MODE_READ_ONLY);
545                                                 Slog.d(
546                                                         TAG,
547                                                         "Successfully opened a file with"
548                                                                 + " ParcelFileDescriptor.");
549                                             } catch (FileNotFoundException e) {
550                                                 Slog.e(TAG, "Cannot open file.", e);
551                                             } finally {
552                                                 future.complete(pfd);
553                                                 if (pfd != null) {
554                                                     try {
555                                                         pfd.close();
556                                                     } catch (IOException ex) {
557                                                         Slog.e(
558                                                                 TAG,
559                                                                 "Error closing"
560                                                                         + " ParcelFileDescriptor.",
561                                                                 ex);
562                                                     }
563                                                 }
564                                             }
565                                         }));
566             }
567         };
568     }
569 }
570