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.ambientcontext;
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.content.Intent;
27 import android.os.Bundle;
28 import android.os.IBinder;
29 import android.os.RemoteCallback;
30 import android.util.Slog;
31 
32 import java.util.Objects;
33 import java.util.function.Consumer;
34 
35 /**
36  * Abstract base class for {@link AmbientContextEvent} detection service.
37  *
38  * <p> A service that provides requested ambient context events to the system.
39  * The system's default AmbientContextDetectionService implementation is configured in
40  * {@code config_defaultAmbientContextDetectionService}. If this config has no value, a stub is
41  * returned.
42  *
43  * See: {@code AmbientContextManagerService}.
44  *
45  * <pre>
46  * {@literal
47  * <service android:name=".YourAmbientContextDetectionService"
48  *          android:permission="android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE">
49  * </service>}
50  * </pre>
51  *
52  * @hide
53  */
54 @SystemApi
55 public abstract class AmbientContextDetectionService extends Service {
56     private static final String TAG = AmbientContextDetectionService.class.getSimpleName();
57 
58     /**
59      * The {@link Intent} that must be declared as handled by the service. To be supported, the
60      * service must also require the
61      * {@link android.Manifest.permission#BIND_AMBIENT_CONTEXT_DETECTION_SERVICE}
62      * permission so that other applications can not abuse it.
63      */
64     public static final String SERVICE_INTERFACE =
65             "android.service.ambientcontext.AmbientContextDetectionService";
66 
67     @Nullable
68     @Override
onBind(@onNull Intent intent)69     public final IBinder onBind(@NonNull Intent intent) {
70         if (SERVICE_INTERFACE.equals(intent.getAction())) {
71             return new IAmbientContextDetectionService.Stub() {
72                 /** {@inheritDoc} */
73                 @Override
74                 public void startDetection(
75                         @NonNull AmbientContextEventRequest request, String packageName,
76                         RemoteCallback detectionResultCallback, RemoteCallback statusCallback) {
77                     Objects.requireNonNull(request);
78                     Objects.requireNonNull(packageName);
79                     Objects.requireNonNull(detectionResultCallback);
80                     Objects.requireNonNull(statusCallback);
81                     Consumer<AmbientContextDetectionResult> detectionResultConsumer =
82                             result -> {
83                                 Bundle bundle = new Bundle();
84                                 bundle.putParcelable(
85                                         AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY,
86                                         result);
87                                 detectionResultCallback.sendResult(bundle);
88                             };
89                     Consumer<AmbientContextDetectionServiceStatus> statusConsumer =
90                             status -> {
91                                 Bundle bundle = new Bundle();
92                                 bundle.putParcelable(
93                                         AmbientContextDetectionServiceStatus
94                                                 .STATUS_RESPONSE_BUNDLE_KEY,
95                                         status);
96                                 statusCallback.sendResult(bundle);
97                             };
98                     AmbientContextDetectionService.this.onStartDetection(
99                             request, packageName, detectionResultConsumer, statusConsumer);
100                     Slog.d(TAG, "startDetection " + request);
101                 }
102 
103                 /** {@inheritDoc} */
104                 @Override
105                 public void stopDetection(String packageName) {
106                     Objects.requireNonNull(packageName);
107                     AmbientContextDetectionService.this.onStopDetection(packageName);
108                 }
109 
110                 /** {@inheritDoc} */
111                 @Override
112                 public void queryServiceStatus(
113                         @AmbientContextEvent.EventCode int[] eventTypes,
114                         String packageName,
115                         RemoteCallback callback) {
116                     Objects.requireNonNull(eventTypes);
117                     Objects.requireNonNull(packageName);
118                     Objects.requireNonNull(callback);
119                     Consumer<AmbientContextDetectionServiceStatus> consumer =
120                             response -> {
121                                 Bundle bundle = new Bundle();
122                                 bundle.putParcelable(
123                                         AmbientContextDetectionServiceStatus
124                                                 .STATUS_RESPONSE_BUNDLE_KEY,
125                                         response);
126                                 callback.sendResult(bundle);
127                             };
128                     AmbientContextDetectionService.this.onQueryServiceStatus(
129                             eventTypes, packageName, consumer);
130                 }
131             };
132         }
133         return null;
134     }
135 
136     /**
137      * Called when a client app requests starting detection of the events in the request. The
138      * implementation should keep track of whether the user has explicitly consented to detecting
139      * the events using on-going ambient sensor (e.g. microphone), and agreed to share the
140      * detection results with this client app. If the user has not consented, the detection
141      * should not start, and the statusConsumer should get a response with STATUS_ACCESS_DENIED.
142      * If the user has made the consent and the underlying services are available, the
143      * implementation should start detection and provide detected events to the
144      * detectionResultConsumer. If the type of event needs immediate attention, the implementation
145      * should send result as soon as detected. Otherwise, the implementation can bulk send response.
146      * The ongoing detection will keep running, until onStopDetection is called. If there were
147      * previously requested detection from the same package, regardless of the type of events in
148      * the request, the previous request will be replaced with the new request.
149      *
150      * @param request The request with events to detect.
151      * @param packageName the requesting app's package name
152      * @param detectionResultConsumer the consumer for the detected event
153      * @param statusConsumer the consumer for the service status.
154      */
155     @BinderThread
156     public abstract void onStartDetection(
157             @NonNull AmbientContextEventRequest request,
158             @NonNull String packageName,
159             @NonNull Consumer<AmbientContextDetectionResult> detectionResultConsumer,
160             @NonNull Consumer<AmbientContextDetectionServiceStatus> statusConsumer);
161 
162     /**
163      * Stops detection of the events. Events that are not being detected will be ignored.
164      *
165      * @param packageName stops detection for the given package.
166      */
167     public abstract void onStopDetection(@NonNull String packageName);
168 
169     /**
170      * Called when a query for the detection status occurs. The implementation should check
171      * the detection status of the requested events for the package, and provide results in a
172      * {@link AmbientContextDetectionServiceStatus} for the consumer.
173      *
174      * @param eventTypes The events to check for status.
175      * @param packageName the requesting app's package name
176      * @param consumer the consumer for the query results
177      */
178     @BinderThread
179     public abstract void onQueryServiceStatus(
180             @NonNull int[] eventTypes,
181             @NonNull String packageName,
182             @NonNull Consumer<AmbientContextDetectionServiceStatus> consumer);
183 }
184