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.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.app.Service;
24 import android.app.ambientcontext.AmbientContextEvent;
25 import android.app.ambientcontext.AmbientContextEventRequest;
26 import android.app.wearable.WearableSensingManager;
27 import android.content.Intent;
28 import android.os.Bundle;
29 import android.os.IBinder;
30 import android.os.ParcelFileDescriptor;
31 import android.os.PersistableBundle;
32 import android.os.RemoteCallback;
33 import android.os.SharedMemory;
34 import android.service.ambientcontext.AmbientContextDetectionResult;
35 import android.service.ambientcontext.AmbientContextDetectionServiceStatus;
36 import android.util.Slog;
37 
38 import java.util.Arrays;
39 import java.util.HashSet;
40 import java.util.Objects;
41 import java.util.Set;
42 import java.util.function.Consumer;
43 
44 /**
45  * Abstract base class for sensing with wearable devices. An example of this is {@link
46  *AmbientContextEvent} detection.
47  *
48  * <p> A service that provides requested sensing events to the system, such as a {@link
49  *AmbientContextEvent}. The system's default WearableSensingService implementation is configured in
50  * {@code config_defaultWearableSensingService}. If this config has no value, a stub is
51  * returned.
52  *
53  * <p> An implementation of a WearableSensingService should be an isolated service. Using the
54  * "isolatedProcess=true" attribute in the service's configurations. </p>
55  **
56  * <pre>
57  * {@literal
58  * <service android:name=".YourWearableSensingService"
59  *          android:permission="android.permission.BIND_WEARABLE_SENSING_SERVICE"
60  *          android:isolatedProcess="true">
61  * </service>}
62  * </pre>
63  *
64  * <p>The use of "Wearable" here is not the same as the Android Wear platform and should be treated
65  * separately. </p>
66  *
67  * @hide
68  */
69 @SystemApi
70 public abstract class WearableSensingService extends Service {
71     private static final String TAG = WearableSensingService.class.getSimpleName();
72 
73     /**
74      * The bundle key for this class of object, used in {@code RemoteCallback#sendResult}.
75      *
76      * @hide
77      */
78     public static final String STATUS_RESPONSE_BUNDLE_KEY =
79             "android.app.wearable.WearableSensingStatusBundleKey";
80 
81     /**
82      * The {@link Intent} that must be declared as handled by the service. To be supported, the
83      * service must also require the
84      * {@link android.Manifest.permission#BIND_WEARABLE_SENSING_SERVICE}
85      * permission so that other applications can not abuse it.
86      */
87     public static final String SERVICE_INTERFACE =
88             "android.service.wearable.WearableSensingService";
89 
90     @Nullable
91     @Override
onBind(@onNull Intent intent)92     public final IBinder onBind(@NonNull Intent intent) {
93         if (SERVICE_INTERFACE.equals(intent.getAction())) {
94             return new IWearableSensingService.Stub() {
95                 /** {@inheritDoc} */
96                 @Override
97                 public void provideDataStream(
98                         ParcelFileDescriptor parcelFileDescriptor,
99                         RemoteCallback callback) {
100                     Objects.requireNonNull(parcelFileDescriptor);
101                     Consumer<Integer> consumer = response -> {
102                         Bundle bundle = new Bundle();
103                         bundle.putInt(
104                                 STATUS_RESPONSE_BUNDLE_KEY,
105                                 response);
106                         callback.sendResult(bundle);
107                     };
108                     WearableSensingService.this.onDataStreamProvided(
109                             parcelFileDescriptor, consumer);
110                 }
111 
112                 /** {@inheritDoc} */
113                 @Override
114                 public void provideData(
115                         PersistableBundle data,
116                         SharedMemory sharedMemory,
117                         RemoteCallback callback) {
118                     Objects.requireNonNull(data);
119                     Consumer<Integer> consumer = response -> {
120                         Bundle bundle = new Bundle();
121                         bundle.putInt(
122                                 STATUS_RESPONSE_BUNDLE_KEY,
123                                 response);
124                         callback.sendResult(bundle);
125                     };
126                     WearableSensingService.this.onDataProvided(data, sharedMemory, consumer);
127                 }
128 
129                 /** {@inheritDoc} */
130                 @Override
131                 public void startDetection(@NonNull AmbientContextEventRequest request,
132                         String packageName, RemoteCallback detectionResultCallback,
133                         RemoteCallback statusCallback) {
134                     Objects.requireNonNull(request);
135                     Objects.requireNonNull(packageName);
136                     Objects.requireNonNull(detectionResultCallback);
137                     Objects.requireNonNull(statusCallback);
138                     Consumer<AmbientContextDetectionResult> detectionResultConsumer = result -> {
139                         Bundle bundle = new Bundle();
140                         bundle.putParcelable(
141                                 AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY, result);
142                         detectionResultCallback.sendResult(bundle);
143                     };
144                     Consumer<AmbientContextDetectionServiceStatus> statusConsumer = status -> {
145                         Bundle bundle = new Bundle();
146                         bundle.putParcelable(
147                                 AmbientContextDetectionServiceStatus.STATUS_RESPONSE_BUNDLE_KEY,
148                                 status);
149                         statusCallback.sendResult(bundle);
150                     };
151                     WearableSensingService.this.onStartDetection(
152                             request, packageName, statusConsumer, detectionResultConsumer);
153                     Slog.d(TAG, "startDetection " + request);
154                 }
155 
156                 /** {@inheritDoc} */
157                 @Override
158                 public void stopDetection(String packageName) {
159                     Objects.requireNonNull(packageName);
160                     WearableSensingService.this.onStopDetection(packageName);
161                 }
162 
163                 /** {@inheritDoc} */
164                 @Override
165                 public void queryServiceStatus(@AmbientContextEvent.EventCode int[] eventTypes,
166                         String packageName, RemoteCallback callback) {
167                     Objects.requireNonNull(eventTypes);
168                     Objects.requireNonNull(packageName);
169                     Objects.requireNonNull(callback);
170                     Consumer<AmbientContextDetectionServiceStatus> consumer = response -> {
171                         Bundle bundle = new Bundle();
172                         bundle.putParcelable(
173                                 AmbientContextDetectionServiceStatus.STATUS_RESPONSE_BUNDLE_KEY,
174                                 response);
175                         callback.sendResult(bundle);
176                     };
177                     Integer[] events = intArrayToIntegerArray(eventTypes);
178                     WearableSensingService.this.onQueryServiceStatus(
179                             new HashSet<>(Arrays.asList(events)), packageName, consumer);
180                 }
181 
182             };
183         }
184         Slog.w(TAG, "Incorrect service interface, returning null.");
185         return null;
186     }
187 
188     /**
189      * Called when a data stream to the wearable is provided. This data stream can be used to obtain
190      * data from a wearable device. It is up to the implementation to maintain the data stream and
191      * close the data stream when it is finished.
192      *
193      * @param parcelFileDescriptor The data stream to the wearable
194      * @param statusConsumer the consumer for the service status.
195      */
196     @BinderThread
197     public abstract void onDataStreamProvided(@NonNull ParcelFileDescriptor parcelFileDescriptor,
198             @NonNull Consumer<Integer> statusConsumer);
199 
200     /**
201      * Called when configurations and read-only data in a {@link PersistableBundle}
202      * can be used by the WearableSensingService and sends the result to the {@link Consumer}
203      * right after the call. It is dependent on the application to define the type of data to
204      * provide. This is used by applications that will also provide an implementation of an isolated
205      * WearableSensingService. If the data was provided successfully
206      * {@link WearableSensingManager#STATUS_SUCCESS} will be provided.
207      *
208      * @param data Application configuration data to provide to the {@link WearableSensingService}.
209      *             PersistableBundle does not allow any remotable objects or other contents
210      *             that can be used to communicate with other processes.
211      * @param sharedMemory The unrestricted data blob to
212      *                     provide to the {@link WearableSensingService}. Use this to provide the
213      *                     sensing models data or other such data to the trusted process.
214      * @param statusConsumer the consumer for the service status.
215      */
216     @BinderThread
217     public abstract void onDataProvided(
218             @NonNull PersistableBundle data,
219             @Nullable SharedMemory sharedMemory,
220             @NonNull Consumer<Integer> statusConsumer);
221 
222     /**
223      * Called when a client app requests starting detection of the events in the request. The
224      * implementation should keep track of whether the user has explicitly consented to detecting
225      * the events using on-going ambient sensor (e.g. microphone), and agreed to share the
226      * detection results with this client app. If the user has not consented, the detection
227      * should not start, and the statusConsumer should get a response with STATUS_ACCESS_DENIED.
228      * If the user has made the consent and the underlying services are available, the
229      * implementation should start detection and provide detected events to the
230      * detectionResultConsumer. If the type of event needs immediate attention, the implementation
231      * should send result as soon as detected. Otherwise, the implementation can batch response.
232      * The ongoing detection will keep running, until onStopDetection is called. If there were
233      * previously requested detections from the same package, regardless of the type of events in
234      * the request, the previous request will be replaced with the new request and pending events
235      * are discarded.
236      *
237      * @param request The request with events to detect.
238      * @param packageName the requesting app's package name
239      * @param statusConsumer the consumer for the service status.
240      * @param detectionResultConsumer the consumer for the detected event
241      */
242     @BinderThread
243     public abstract void onStartDetection(@NonNull AmbientContextEventRequest request,
244             @NonNull String packageName,
245             @NonNull Consumer<AmbientContextDetectionServiceStatus> statusConsumer,
246             @NonNull Consumer<AmbientContextDetectionResult> detectionResultConsumer);
247 
248     /**
249      * Stops detection of the events. Events that are not being detected will be ignored.
250      *
251      * @param packageName stops detection for the given package.
252      */
253     public abstract void onStopDetection(@NonNull String packageName);
254 
255     /**
256      * Called when a query for the detection status occurs. The implementation should check
257      * the detection status of the requested events for the package, and provide results in a
258      * {@link AmbientContextDetectionServiceStatus} for the consumer.
259      *
260      * @param eventTypes The events to check for status.
261      * @param packageName the requesting app's package name
262      * @param consumer the consumer for the query results
263      */
264     @BinderThread
265     public abstract void onQueryServiceStatus(@NonNull Set<Integer> eventTypes,
266             @NonNull String packageName,
267             @NonNull Consumer<AmbientContextDetectionServiceStatus> consumer);
268 
269     @NonNull
270     private static Integer[] intArrayToIntegerArray(@NonNull int[] integerSet) {
271         Integer[] intArray = new Integer[integerSet.length];
272         int i = 0;
273         for (Integer type : integerSet) {
274             intArray[i++] = type;
275         }
276         return intArray;
277     }
278 }
279