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.service.wearable;
18 
19 import android.annotation.BinderThread;
20 import android.annotation.FlaggedApi;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SuppressLint;
24 import android.annotation.SystemApi;
25 import android.app.Service;
26 import android.app.ambientcontext.AmbientContextEvent;
27 import android.app.ambientcontext.AmbientContextEventRequest;
28 import android.app.wearable.Flags;
29 import android.app.wearable.IWearableSensingCallback;
30 import android.app.wearable.WearableSensingDataRequest;
31 import android.app.wearable.WearableSensingManager;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.os.Bundle;
35 import android.os.IBinder;
36 import android.os.ParcelFileDescriptor;
37 import android.os.PersistableBundle;
38 import android.os.Process;
39 import android.os.RemoteCallback;
40 import android.os.RemoteException;
41 import android.os.SharedMemory;
42 import android.service.ambientcontext.AmbientContextDetectionResult;
43 import android.service.ambientcontext.AmbientContextDetectionServiceStatus;
44 import android.service.voice.HotwordAudioStream;
45 import android.text.TextUtils;
46 import android.util.Slog;
47 import android.util.SparseArray;
48 
49 import com.android.internal.infra.AndroidFuture;
50 
51 import java.io.FileInputStream;
52 import java.io.FileNotFoundException;
53 import java.time.Duration;
54 import java.util.Arrays;
55 import java.util.HashSet;
56 import java.util.Objects;
57 import java.util.Set;
58 import java.util.concurrent.ExecutionException;
59 import java.util.concurrent.Executor;
60 import java.util.concurrent.TimeUnit;
61 import java.util.concurrent.TimeoutException;
62 import java.util.function.Consumer;
63 
64 /**
65  * Abstract base class for sensing with wearable devices. An example of this is {@link
66  *AmbientContextEvent} detection.
67  *
68  * <p> A service that provides requested sensing events to the system, such as a {@link
69  *AmbientContextEvent}. The system's default WearableSensingService implementation is configured in
70  * {@code config_defaultWearableSensingService}. If this config has no value, a stub is
71  * returned.
72  *
73  * <p> An implementation of a WearableSensingService should be an isolated service. Using the
74  * "isolatedProcess=true" attribute in the service's configurations. </p>
75  **
76  * <pre>
77  * {@literal
78  * <service android:name=".YourWearableSensingService"
79  *          android:permission="android.permission.BIND_WEARABLE_SENSING_SERVICE"
80  *          android:isolatedProcess="true">
81  * </service>}
82  * </pre>
83  *
84  * <p>The use of "Wearable" here is not the same as the Android Wear platform and should be treated
85  * separately. </p>
86  *
87  * @hide
88  */
89 @SystemApi
90 public abstract class WearableSensingService extends Service {
91     private static final String TAG = WearableSensingService.class.getSimpleName();
92 
93     /**
94      * The bundle key for this class of object, used in {@code RemoteCallback#sendResult}.
95      *
96      * @hide
97      */
98     public static final String STATUS_RESPONSE_BUNDLE_KEY =
99             "android.app.wearable.WearableSensingStatusBundleKey";
100 
101     /**
102      * The bundle key for hotword audio stream, used in {@code RemoteCallback#sendResult}.
103      *
104      * @hide
105      */
106     public static final String HOTWORD_AUDIO_STREAM_BUNDLE_KEY =
107             "android.app.wearable.HotwordAudioStreamBundleKey";
108 
109     /**
110      * The {@link Intent} that must be declared as handled by the service. To be supported, the
111      * service must also require the
112      * {@link android.Manifest.permission#BIND_WEARABLE_SENSING_SERVICE}
113      * permission so that other applications can not abuse it.
114      */
115     public static final String SERVICE_INTERFACE =
116             "android.service.wearable.WearableSensingService";
117 
118     // Timeout to prevent thread from waiting on the openFile future indefinitely.
119     private static final Duration OPEN_FILE_TIMEOUT = Duration.ofSeconds(5);
120 
121     private final SparseArray<WearableSensingDataRequester> mDataRequestObserverIdToRequesterMap =
122             new SparseArray<>();
123 
124     private IWearableSensingCallback mWearableSensingCallback;
125 
126     @Nullable
127     @Override
onBind(@onNull Intent intent)128     public final IBinder onBind(@NonNull Intent intent) {
129         if (SERVICE_INTERFACE.equals(intent.getAction())) {
130             return new IWearableSensingService.Stub() {
131                 /** {@inheritDoc} */
132                 @Override
133                 public void provideSecureConnection(
134                         ParcelFileDescriptor secureWearableConnection,
135                         IWearableSensingCallback wearableSensingCallback,
136                         RemoteCallback callback) {
137                     Objects.requireNonNull(secureWearableConnection);
138                     if (wearableSensingCallback != null) {
139                         mWearableSensingCallback = wearableSensingCallback;
140                     }
141                     Consumer<Integer> consumer = createWearableStatusConsumer(callback);
142                     WearableSensingService.this.onSecureConnectionProvided(
143                             secureWearableConnection, consumer);
144                 }
145 
146                 /** {@inheritDoc} */
147                 @Override
148                 public void provideDataStream(
149                         ParcelFileDescriptor parcelFileDescriptor,
150                         IWearableSensingCallback wearableSensingCallback,
151                         RemoteCallback callback) {
152                     Objects.requireNonNull(parcelFileDescriptor);
153                     if (wearableSensingCallback != null) {
154                         mWearableSensingCallback = wearableSensingCallback;
155                     }
156                     Consumer<Integer> consumer = createWearableStatusConsumer(callback);
157                     WearableSensingService.this.onDataStreamProvided(
158                             parcelFileDescriptor, consumer);
159                 }
160 
161                 /** {@inheritDoc} */
162                 @Override
163                 public void provideData(
164                         PersistableBundle data,
165                         SharedMemory sharedMemory,
166                         RemoteCallback callback) {
167                     Objects.requireNonNull(data);
168                     Consumer<Integer> consumer = createWearableStatusConsumer(callback);
169                     WearableSensingService.this.onDataProvided(data, sharedMemory, consumer);
170                 }
171 
172                 /** {@inheritDoc} */
173                 @Override
174                 public void registerDataRequestObserver(
175                         int dataType,
176                         RemoteCallback dataRequestCallback,
177                         int dataRequestObserverId,
178                         String packageName,
179                         RemoteCallback statusCallback) {
180                     Objects.requireNonNull(dataRequestCallback);
181                     Objects.requireNonNull(statusCallback);
182                     WearableSensingDataRequester dataRequester;
183                     synchronized (mDataRequestObserverIdToRequesterMap) {
184                         dataRequester =
185                                 mDataRequestObserverIdToRequesterMap.get(dataRequestObserverId);
186                         if (dataRequester == null) {
187                             dataRequester = createDataRequester(dataRequestCallback);
188                             mDataRequestObserverIdToRequesterMap.put(
189                                     dataRequestObserverId, dataRequester);
190                         }
191                     }
192                     Consumer<Integer> statusConsumer = createWearableStatusConsumer(statusCallback);
193                     WearableSensingService.this.onDataRequestObserverRegistered(
194                             dataType, packageName, dataRequester, statusConsumer);
195                 }
196 
197                 @Override
198                 public void unregisterDataRequestObserver(
199                         int dataType,
200                         int dataRequestObserverId,
201                         String packageName,
202                         RemoteCallback statusCallback) {
203                     WearableSensingDataRequester dataRequester;
204                     synchronized (mDataRequestObserverIdToRequesterMap) {
205                         dataRequester =
206                                 mDataRequestObserverIdToRequesterMap.get(dataRequestObserverId);
207                         if (dataRequester == null) {
208                             Slog.w(
209                                     TAG,
210                                     "dataRequestObserverId not found, cannot unregister data"
211                                             + " request observer.");
212                             return;
213                         }
214                         mDataRequestObserverIdToRequesterMap.remove(dataRequestObserverId);
215                     }
216                     Consumer<Integer> statusConsumer = createWearableStatusConsumer(statusCallback);
217                     WearableSensingService.this.onDataRequestObserverUnregistered(
218                             dataType, packageName, dataRequester, statusConsumer);
219                 }
220 
221                 @Override
222                 public void startHotwordRecognition(
223                         RemoteCallback wearableHotwordCallback, RemoteCallback statusCallback) {
224                     Consumer<HotwordAudioStream> hotwordAudioConsumer =
225                             (hotwordAudioStream) -> {
226                                 Bundle bundle = new Bundle();
227                                 bundle.putParcelable(
228                                         HOTWORD_AUDIO_STREAM_BUNDLE_KEY, hotwordAudioStream);
229                                 wearableHotwordCallback.sendResult(bundle);
230                             };
231                     Consumer<Integer> statusConsumer =
232                             response -> {
233                                 Bundle bundle = new Bundle();
234                                 bundle.putInt(STATUS_RESPONSE_BUNDLE_KEY, response);
235                                 statusCallback.sendResult(bundle);
236                             };
237                     WearableSensingService.this.onStartHotwordRecognition(
238                             hotwordAudioConsumer, statusConsumer);
239                 }
240 
241                 /** {@inheritDoc} */
242                 @Override
243                 public void stopHotwordRecognition(RemoteCallback statusCallback) {
244                     Consumer<Integer> statusConsumer =
245                             response -> {
246                                 Bundle bundle = new Bundle();
247                                 bundle.putInt(STATUS_RESPONSE_BUNDLE_KEY, response);
248                                 statusCallback.sendResult(bundle);
249                             };
250                     WearableSensingService.this.onStopHotwordRecognition(statusConsumer);
251                 }
252 
253                 /** {@inheritDoc} */
254                 @Override
255                 public void onValidatedByHotwordDetectionService() {
256                     WearableSensingService.this.onValidatedByHotwordDetectionService();
257                 }
258 
259                 /** {@inheritDoc} */
260                 @Override
261                 public void stopActiveHotwordAudio() {
262                     WearableSensingService.this.onStopHotwordAudioStream();
263                 }
264 
265                 /** {@inheritDoc} */
266                 @Override
267                 public void startDetection(
268                         @NonNull AmbientContextEventRequest request,
269                         String packageName,
270                         RemoteCallback detectionResultCallback,
271                         RemoteCallback statusCallback) {
272                     Objects.requireNonNull(request);
273                     Objects.requireNonNull(packageName);
274                     Objects.requireNonNull(detectionResultCallback);
275                     Objects.requireNonNull(statusCallback);
276                     Consumer<AmbientContextDetectionResult> detectionResultConsumer =
277                             result -> {
278                                 Bundle bundle = new Bundle();
279                                 bundle.putParcelable(
280                                         AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY,
281                                         result);
282                                 detectionResultCallback.sendResult(bundle);
283                             };
284                     Consumer<AmbientContextDetectionServiceStatus> statusConsumer =
285                             status -> {
286                                 Bundle bundle = new Bundle();
287                                 bundle.putParcelable(
288                                         AmbientContextDetectionServiceStatus
289                                                 .STATUS_RESPONSE_BUNDLE_KEY,
290                                         status);
291                                 statusCallback.sendResult(bundle);
292                             };
293                     WearableSensingService.this.onStartDetection(
294                             request, packageName, statusConsumer, detectionResultConsumer);
295                     Slog.d(TAG, "startDetection " + request);
296                 }
297 
298                 /** {@inheritDoc} */
299                 @Override
300                 public void stopDetection(String packageName) {
301                     Objects.requireNonNull(packageName);
302                     WearableSensingService.this.onStopDetection(packageName);
303                 }
304 
305                 /** {@inheritDoc} */
306                 @Override
307                 public void queryServiceStatus(
308                         @AmbientContextEvent.EventCode int[] eventTypes,
309                         String packageName,
310                         RemoteCallback callback) {
311                     Objects.requireNonNull(eventTypes);
312                     Objects.requireNonNull(packageName);
313                     Objects.requireNonNull(callback);
314                     Consumer<AmbientContextDetectionServiceStatus> consumer =
315                             response -> {
316                                 Bundle bundle = new Bundle();
317                                 bundle.putParcelable(
318                                         AmbientContextDetectionServiceStatus
319                                                 .STATUS_RESPONSE_BUNDLE_KEY,
320                                         response);
321                                 callback.sendResult(bundle);
322                             };
323                     Integer[] events = intArrayToIntegerArray(eventTypes);
324                     WearableSensingService.this.onQueryServiceStatus(
325                             new HashSet<>(Arrays.asList(events)), packageName, consumer);
326                 }
327 
328                 /** {@inheritDoc} */
329                 @Override
330                 public void killProcess() {
331                     Slog.d(TAG, "#killProcess");
332                     Process.killProcess(Process.myPid());
333                 }
334             };
335         }
336         Slog.w(TAG, "Incorrect service interface, returning null.");
337         return null;
338     }
339 
340     /**
341      * Called when a secure connection to the wearable is available. See {@link
342      * WearableSensingManager#provideConnection(ParcelFileDescriptor, Executor, Consumer)}
343      * for details about the secure connection.
344      *
345      * <p>When the {@code secureWearableConnection} is closed, the system will send a {@link
346      * WearableSensingManager#STATUS_CHANNEL_ERROR} status code to the status consumer provided by
347      * the caller of {@link WearableSensingManager#provideConnection(ParcelFileDescriptor,
348      * Executor, Consumer)}.
349      *
350      * <p>The implementing class should override this method. It should return an appropriate status
351      * code via {@code statusConsumer} after receiving the {@code secureWearableConnection}.
352      *
353      * @param secureWearableConnection The secure connection to the wearable.
354      * @param statusConsumer The consumer for the service status.
355      */
356     @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
357     @BinderThread
358     public void onSecureConnectionProvided(
359             @NonNull ParcelFileDescriptor secureWearableConnection,
360             @NonNull Consumer<Integer> statusConsumer) {
361         statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
362     }
363 
364     /**
365      * Called when a data stream to the wearable is provided. This data stream can be used to obtain
366      * data from a wearable device. It is up to the implementation to maintain the data stream and
367      * close the data stream when it is finished.
368      *
369      * @param parcelFileDescriptor The data stream to the wearable
370      * @param statusConsumer the consumer for the service status.
371      */
372     @BinderThread
373     public abstract void onDataStreamProvided(@NonNull ParcelFileDescriptor parcelFileDescriptor,
374             @NonNull Consumer<Integer> statusConsumer);
375 
376     /**
377      * Called when configurations and read-only data in a {@link PersistableBundle} can be used by
378      * the WearableSensingService and sends the result to the {@link Consumer} right after the call.
379      * It is dependent on the application to define the type of data to provide. This is used by
380      * applications that will also provide an implementation of an isolated WearableSensingService.
381      * If the data was provided successfully {@link WearableSensingManager#STATUS_SUCCESS} will be
382      * provided.
383      *
384      * @param data Application configuration data to provide to the {@link WearableSensingService}.
385      *     PersistableBundle does not allow any remotable objects or other contents that can be used
386      *     to communicate with other processes.
387      * @param sharedMemory The unrestricted data blob to provide to the {@link
388      *     WearableSensingService}. Use this to provide the sensing models data or other such data
389      *     to the trusted process.
390      * @param statusConsumer the consumer for the service status.
391      */
392     @BinderThread
393     public abstract void onDataProvided(
394             @NonNull PersistableBundle data,
395             @Nullable SharedMemory sharedMemory,
396             @NonNull Consumer<Integer> statusConsumer);
397 
398     /**
399      * Called when a data request observer is registered. Each request must not be larger than
400      * {@link WearableSensingDataRequest#getMaxRequestSize()}. In addition, at most {@link
401      * WearableSensingDataRequest#getRateLimit()} requests can be sent every rolling {@link
402      * WearableSensingDataRequest#getRateLimitWindowSize()}. Requests that are too large or too
403      * frequent will be dropped by the system. See {@link
404      * WearableSensingDataRequester#requestData(WearableSensingDataRequest, Consumer)} for details
405      * about the status code returned for each request.
406      *
407      * <p>The implementing class should override this method. After the data requester is received,
408      * it should send a {@link WearableSensingManager#STATUS_SUCCESS} status code to the {@code
409      * statusConsumer} unless it encounters an error condition described by a status code listed in
410      * {@link WearableSensingManager}, such as {@link
411      * WearableSensingManager#STATUS_WEARABLE_UNAVAILABLE}, in which case it should return the
412      * corresponding status code.
413      *
414      * @param dataType The data type the observer is registered for. Values are defined by the
415      *     application that implements this class.
416      * @param packageName The package name of the app that will receive the requests.
417      * @param dataRequester A handle to the observer registered. It can be used to request data of
418      *     the specified data type.
419      * @param statusConsumer the consumer for the status of the data request observer registration.
420      *     This is different from the status for each data request.
421      */
422     @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
423     @BinderThread
424     public void onDataRequestObserverRegistered(
425             int dataType,
426             @NonNull String packageName,
427             @NonNull WearableSensingDataRequester dataRequester,
428             @NonNull Consumer<Integer> statusConsumer) {
429         statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
430     }
431 
432     /**
433      * Called when a data request observer is unregistered.
434      *
435      * <p>The implementing class should override this method. It should send a {@link
436      * WearableSensingManager#STATUS_SUCCESS} status code to the {@code statusConsumer} unless it
437      * encounters an error condition described by a status code listed in {@link
438      * WearableSensingManager}, such as {@link WearableSensingManager#STATUS_WEARABLE_UNAVAILABLE},
439      * in which case it should return the corresponding status code.
440      *
441      * @param dataType The data type the observer is for.
442      * @param packageName The package name of the app that will receive the requests sent to the
443      *     dataRequester.
444      * @param dataRequester A handle to the observer to be unregistered. It is the exact same
445      *     instance provided in a previous {@link #onDataRequestObserverRegistered(int, String,
446      *     WearableSensingDataRequester, Consumer)} invocation.
447      * @param statusConsumer the consumer for the status of the data request observer
448      *     unregistration. This is different from the status for each data request.
449      */
450     @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
451     @BinderThread
452     public void onDataRequestObserverUnregistered(
453             int dataType,
454             @NonNull String packageName,
455             @NonNull WearableSensingDataRequester dataRequester,
456             @NonNull Consumer<Integer> statusConsumer) {
457         statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
458     }
459 
460     /**
461      * Called when the wearable is requested to start hotword recognition.
462      *
463      * <p>This method is expected to be overridden by a derived class. The implementation should
464      * store the {@code hotwordAudioConsumer} and send it the audio data when first-stage hotword is
465      * detected from the wearable. It should also send a {@link
466      * WearableSensingManager#STATUS_SUCCESS} status code to the {@code statusConsumer} unless it
467      * encounters an error condition described by a status code listed in {@link
468      * WearableSensingManager}, such as {@link WearableSensingManager#STATUS_WEARABLE_UNAVAILABLE},
469      * in which case it should return the corresponding status code.
470      *
471      * <p>The implementation should also store the {@code statusConsumer}. If the wearable stops
472      * listening for hotword for any reason other than {@link #onStopHotwordRecognition(Consumer)}
473      * being invoked, it should send an appropriate status code listed in {@link
474      * WearableSensingManager} to {@code statusConsumer}. If the error condition cannot be described
475      * by any of those status codes, it should send a {@link WearableSensingManager#STATUS_UNKNOWN}.
476      *
477      * <p>If this method is called again, the implementation should use the new {@code
478      * hotwordAudioConsumer} and discard any previous ones it received.
479      *
480      * <p>At this time, the {@code timestamp} field in the {@link HotwordAudioStream} is not used
481      * and will be discarded by the system.
482      *
483      * @param hotwordAudioConsumer The consumer for the wearable hotword audio data.
484      * @param statusConsumer The consumer for the service status.
485      */
486     @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
487     @BinderThread
488     public void onStartHotwordRecognition(
489             @NonNull Consumer<HotwordAudioStream> hotwordAudioConsumer,
490             @NonNull Consumer<Integer> statusConsumer) {
491         if (Flags.enableUnsupportedOperationStatusCode()) {
492             statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
493         }
494     }
495 
496     /**
497      * Called when the wearable is requested to stop hotword recognition.
498      *
499      * <p>This method is expected to be overridden by a derived class. It should send a {@link
500      * WearableSensingManager#STATUS_SUCCESS} status code to the {@code statusConsumer} unless it
501      * encounters an error condition described by a status code listed in {@link
502      * WearableSensingManager}, such as {@link WearableSensingManager#STATUS_WEARABLE_UNAVAILABLE},
503      * in which case it should return the corresponding status code.
504      *
505      * @param statusConsumer The consumer for the service status.
506      */
507     @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
508     @BinderThread
509     public void onStopHotwordRecognition(@NonNull Consumer<Integer> statusConsumer) {
510         if (Flags.enableUnsupportedOperationStatusCode()) {
511             statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
512         }
513     }
514 
515     /**
516      * Called when hotword audio data sent to the {@code hotwordAudioConsumer} in {@link
517      * #onStartHotwordRecognition(Consumer, Consumer)} is accepted by the
518      * {@link android.service.voice.HotwordDetectionService} as valid hotword.
519      *
520      * <p>After the implementation of this class sends the hotword audio data to the {@code
521      * hotwordAudioConsumer} in {@link #onStartHotwordRecognition(Consumer,
522      * Consumer)}, the system will forward the data into {@link
523      * android.service.voice.HotwordDetectionService} (which runs in an isolated process) for
524      * second-stage hotword detection. If accepted as valid hotword there, this method will be
525      * called, and then the system will send the data to the currently active {@link
526      * android.service.voice.AlwaysOnHotwordDetector} (which may not run in an isolated process).
527      *
528      * <p>This method is expected to be overridden by a derived class. The implementation must
529      * request the wearable to turn on the microphone indicator to notify the user that audio data
530      * is being used outside of the isolated environment.
531      */
532     @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
533     @BinderThread
534     public void onValidatedByHotwordDetectionService() {}
535 
536     /**
537      * Called when the currently active hotword audio stream is no longer needed.
538      *
539      * <p>This method can be called as a result of hotword rejection by {@link
540      * android.service.voice.HotwordDetectionService}, or the {@link
541      * android.service.voice.AlwaysOnHotwordDetector} closing the data stream it received, or a
542      * non-recoverable error occurred before the data reaches the {@link
543      * android.service.voice.HotwordDetectionService} or the {@link
544      * android.service.voice.AlwaysOnHotwordDetector}.
545      *
546      * <p>This method is expected to be overridden by a derived class. The implementation should
547      * stop sending hotword audio data to the {@code hotwordAudioConsumer} in {@link
548      * #onStartHotwordRecognition(Consumer, Consumer)}
549      */
550     @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
551     @BinderThread
552     public void onStopHotwordAudioStream() {}
553 
554     /**
555      * Called when a client app requests starting detection of the events in the request. The
556      * implementation should keep track of whether the user has explicitly consented to detecting
557      * the events using on-going ambient sensor (e.g. microphone), and agreed to share the
558      * detection results with this client app. If the user has not consented, the detection
559      * should not start, and the statusConsumer should get a response with STATUS_ACCESS_DENIED.
560      * If the user has made the consent and the underlying services are available, the
561      * implementation should start detection and provide detected events to the
562      * detectionResultConsumer. If the type of event needs immediate attention, the implementation
563      * should send result as soon as detected. Otherwise, the implementation can batch response.
564      * The ongoing detection will keep running, until onStopDetection is called. If there were
565      * previously requested detections from the same package, regardless of the type of events in
566      * the request, the previous request will be replaced with the new request and pending events
567      * are discarded.
568      *
569      * @param request The request with events to detect.
570      * @param packageName the requesting app's package name
571      * @param statusConsumer the consumer for the service status.
572      * @param detectionResultConsumer the consumer for the detected event
573      */
574     @BinderThread
575     public abstract void onStartDetection(@NonNull AmbientContextEventRequest request,
576             @NonNull String packageName,
577             @NonNull Consumer<AmbientContextDetectionServiceStatus> statusConsumer,
578             @NonNull Consumer<AmbientContextDetectionResult> detectionResultConsumer);
579 
580     /**
581      * Stops detection of the events. Events that are not being detected will be ignored.
582      *
583      * @param packageName stops detection for the given package.
584      */
585     public abstract void onStopDetection(@NonNull String packageName);
586 
587     /**
588      * Called when a query for the detection status occurs. The implementation should check
589      * the detection status of the requested events for the package, and provide results in a
590      * {@link AmbientContextDetectionServiceStatus} for the consumer.
591      *
592      * @param eventTypes The events to check for status.
593      * @param packageName the requesting app's package name
594      * @param consumer the consumer for the query results
595      */
596     @BinderThread
597     public abstract void onQueryServiceStatus(@NonNull Set<Integer> eventTypes,
598             @NonNull String packageName,
599             @NonNull Consumer<AmbientContextDetectionServiceStatus> consumer);
600 
601     /**
602      * Overrides {@link Context#openFileInput} to read files with the given {@code fileName} under
603      * the internal app storage of the APK providing the implementation for this class. {@link
604      * Context#getFilesDir()} will be added as a prefix to the provided {@code fileName}.
605      *
606      * <p>This method is only functional after {@link
607      * #onSecureConnectionProvided(ParcelFileDescriptor, Consumer)} or {@link
608      * #onDataStreamProvided(ParcelFileDescriptor, Consumer)} has been called as a result of a
609      * process owned by the same APK calling {@link
610      * WearableSensingManager#provideConnection(ParcelFileDescriptor, Executor, Consumer)} or {@link
611      * WearableSensingManager#provideDataStream(ParcelFileDescriptor, Executor, Consumer)}.
612      * Otherwise, it will throw an {@link IllegalStateException}. This is because this method
613      * proxies the file read via that process. Also, the APK needs to have a targetSdkVersion of 35
614      * or newer.
615      *
616      * @param fileName Relative path of a file under {@link Context#getFilesDir()}.
617      * @throws IllegalStateException if the above condition is not satisfied.
618      * @throws FileNotFoundException if the file does not exist or cannot be opened, or an error
619      *     occurred during the RPC to proxy the file read via a non-isolated process.
620      */
621     // SuppressLint is needed because the parent Context class does not specify the nullability of
622     // the parameter filename. If we remove the @NonNull annotation, the linter will complain about
623     // MissingNullability
624     @Override
625     public @NonNull FileInputStream openFileInput(
626             @SuppressLint("InvalidNullabilityOverride") @NonNull String fileName)
627             throws FileNotFoundException {
628         if (fileName == null) {
629             throw new IllegalArgumentException("filename cannot be null");
630         }
631         try {
632             if (mWearableSensingCallback == null) {
633                 throw new IllegalStateException(
634                         "Cannot open file from WearableSensingService. WearableSensingCallback is"
635                                 + " not available.");
636             }
637             AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>();
638             mWearableSensingCallback.openFile(fileName, future);
639             ParcelFileDescriptor pfd =
640                     future.get(OPEN_FILE_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
641             if (pfd == null) {
642                 throw new FileNotFoundException(
643                         TextUtils.formatSimple(
644                                 "File %s not found or unable to be opened in read-only mode.",
645                                 fileName));
646             }
647             return new FileInputStream(pfd.getFileDescriptor());
648         } catch (RemoteException | ExecutionException | TimeoutException e) {
649             throw (FileNotFoundException)
650                     new FileNotFoundException("Cannot open file due to remote service failure")
651                             .initCause(e);
652         } catch (InterruptedException e) {
653             Thread.currentThread().interrupt();
654             throw (FileNotFoundException)
655                     new FileNotFoundException("Interrupted when opening a file.").initCause(e);
656         }
657     }
658 
659     @NonNull
660     private static Integer[] intArrayToIntegerArray(@NonNull int[] integerSet) {
661         Integer[] intArray = new Integer[integerSet.length];
662         int i = 0;
663         for (Integer type : integerSet) {
664             intArray[i++] = type;
665         }
666         return intArray;
667     }
668 
669     private static WearableSensingDataRequester createDataRequester(
670             RemoteCallback dataRequestCallback) {
671         return (request, requestStatusConsumer) -> {
672             Bundle bundle = new Bundle();
673             bundle.putParcelable(WearableSensingDataRequest.REQUEST_BUNDLE_KEY, request);
674             RemoteCallback requestStatusCallback =
675                     new RemoteCallback(
676                             requestStatusBundle -> {
677                                 requestStatusConsumer.accept(
678                                         requestStatusBundle.getInt(
679                                                 WearableSensingManager.STATUS_RESPONSE_BUNDLE_KEY));
680                             });
681             bundle.putParcelable(
682                     WearableSensingDataRequest.REQUEST_STATUS_CALLBACK_BUNDLE_KEY,
683                     requestStatusCallback);
684             dataRequestCallback.sendResult(bundle);
685         };
686     }
687 
688     @NonNull
689     private static Consumer<Integer> createWearableStatusConsumer(RemoteCallback statusCallback) {
690         return response -> {
691             Bundle bundle = new Bundle();
692             bundle.putInt(STATUS_RESPONSE_BUNDLE_KEY, response);
693             statusCallback.sendResult(bundle);
694         };
695     }
696 
697 
698 }
699