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 com.android.server.wearable;
18 
19 import static android.content.Context.BIND_FOREGROUND_SERVICE;
20 import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
21 
22 import android.app.wearable.Flags;
23 import android.app.wearable.IWearableSensingCallback;
24 import android.app.wearable.WearableSensingManager;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.os.ParcelFileDescriptor;
29 import android.os.PersistableBundle;
30 import android.os.RemoteCallback;
31 import android.os.SharedMemory;
32 import android.service.wearable.IWearableSensingService;
33 import android.service.wearable.WearableSensingService;
34 import android.util.Slog;
35 
36 import com.android.internal.annotations.GuardedBy;
37 import com.android.internal.infra.ServiceConnector;
38 
39 import java.io.IOException;
40 
41 /** Manages the connection to the remote wearable sensing service. */
42 final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearableSensingService> {
43     private static final String TAG =
44             com.android.server.wearable.RemoteWearableSensingService.class.getSimpleName();
45     private final static boolean DEBUG = false;
46 
47     private final Object mSecureConnectionLock = new Object();
48 
49     // mNextSecureConnectionContext will only be non-null when we are waiting for the
50     // WearableSensingService process to restart. It will be set to null after it is passed into
51     // WearableSensingService.
52     @GuardedBy("mSecureConnectionLock")
53     private SecureWearableConnectionContext mNextSecureConnectionContext;
54 
55     @GuardedBy("mSecureConnectionLock")
56     private boolean mSecureConnectionProvided = false;
57 
RemoteWearableSensingService(Context context, ComponentName serviceName, int userId)58     RemoteWearableSensingService(Context context, ComponentName serviceName,
59             int userId) {
60         super(context, new Intent(
61                         WearableSensingService.SERVICE_INTERFACE).setComponent(serviceName),
62                 BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
63                 IWearableSensingService.Stub::asInterface);
64 
65         // Bind right away
66         connect();
67     }
68 
69     @Override
getAutoDisconnectTimeoutMs()70     protected long getAutoDisconnectTimeoutMs() {
71         // Disable automatic unbinding.
72         return -1;
73     }
74 
75     /**
76      * Provides a secure connection to the wearable.
77      *
78      * @param secureWearableConnection The secure connection to the wearable
79      * @param wearableSensingCallback The callback for requests such as openFile from the
80      *     WearableSensingService.
81      * @param statusCallback The callback for service status
82      */
provideSecureConnection( ParcelFileDescriptor secureWearableConnection, IWearableSensingCallback wearableSensingCallback, RemoteCallback statusCallback)83     public void provideSecureConnection(
84             ParcelFileDescriptor secureWearableConnection,
85             IWearableSensingCallback wearableSensingCallback,
86             RemoteCallback statusCallback) {
87         if (DEBUG) {
88             Slog.i(TAG, "#provideSecureConnection");
89         }
90         if (!Flags.enableRestartWssProcess()) {
91             Slog.d(
92                     TAG,
93                     "FLAG_ENABLE_RESTART_WSS_PROCESS is disabled. Do not attempt to restart the"
94                         + " WearableSensingService process");
95             provideSecureConnectionInternal(
96                     secureWearableConnection, wearableSensingCallback, statusCallback);
97             return;
98         }
99         synchronized (mSecureConnectionLock) {
100             if (mNextSecureConnectionContext != null) {
101                 // A process restart is in progress, #binderDied is about to be called. Replace
102                 // the previous mNextSecureConnectionContext with the current one
103                 Slog.i(
104                         TAG,
105                         "A new wearable connection is provided before the process restart triggered"
106                             + " by the previous connection is complete. Discarding the previous"
107                             + " connection.");
108                 if (Flags.enableProvideWearableConnectionApi()) {
109                     WearableSensingManagerPerUserService.notifyStatusCallback(
110                             mNextSecureConnectionContext.mStatusCallback,
111                             WearableSensingManager.STATUS_CHANNEL_ERROR);
112                 }
113                 mNextSecureConnectionContext =
114                         new SecureWearableConnectionContext(
115                                 secureWearableConnection, wearableSensingCallback, statusCallback);
116                 return;
117             }
118             if (!mSecureConnectionProvided) {
119                 // no need to kill the process
120                 provideSecureConnectionInternal(
121                         secureWearableConnection, wearableSensingCallback, statusCallback);
122                 mSecureConnectionProvided = true;
123                 return;
124             }
125             mNextSecureConnectionContext =
126                     new SecureWearableConnectionContext(
127                             secureWearableConnection, wearableSensingCallback, statusCallback);
128             // Killing the process causes the binder to die. #binderDied will then be triggered
129             killWearableSensingServiceProcess();
130         }
131     }
132 
provideSecureConnectionInternal( ParcelFileDescriptor secureWearableConnection, IWearableSensingCallback wearableSensingCallback, RemoteCallback statusCallback)133     private void provideSecureConnectionInternal(
134             ParcelFileDescriptor secureWearableConnection,
135             IWearableSensingCallback wearableSensingCallback,
136             RemoteCallback statusCallback) {
137         Slog.d(TAG, "Providing secure wearable connection.");
138         var unused =
139                 post(
140                         service -> {
141                             service.provideSecureConnection(
142                                     secureWearableConnection,
143                                     wearableSensingCallback,
144                                     statusCallback);
145                             try {
146                                 // close the local fd after it has been sent to the WSS process
147                                 secureWearableConnection.close();
148                             } catch (IOException ex) {
149                                 Slog.w(TAG, "Unable to close the local parcelFileDescriptor.", ex);
150                             }
151                         });
152     }
153 
154     @Override
binderDied()155     public void binderDied() {
156         super.binderDied();
157         synchronized (mSecureConnectionLock) {
158             if (mNextSecureConnectionContext != null) {
159                 // This will call #post, which will recreate the process and bind to it
160                 provideSecureConnectionInternal(
161                         mNextSecureConnectionContext.mSecureConnection,
162                         mNextSecureConnectionContext.mWearableSensingCallback,
163                         mNextSecureConnectionContext.mStatusCallback);
164                 mNextSecureConnectionContext = null;
165             } else {
166                 mSecureConnectionProvided = false;
167                 Slog.w(TAG, "Binder died but there is no secure wearable connection to provide.");
168             }
169         }
170     }
171 
172     /** Kills the WearableSensingService process. */
killWearableSensingServiceProcess()173     public void killWearableSensingServiceProcess() {
174         var unused = post(service -> service.killProcess());
175     }
176 
177     /**
178      * Provides the implementation a data stream to the wearable.
179      *
180      * @param parcelFileDescriptor The data stream to the wearable
181      * @param wearableSensingCallback The callback for requests such as openFile from the
182      *     WearableSensingService.
183      * @param callback The callback for service status
184      */
provideDataStream( ParcelFileDescriptor parcelFileDescriptor, IWearableSensingCallback wearableSensingCallback, RemoteCallback callback)185     public void provideDataStream(
186             ParcelFileDescriptor parcelFileDescriptor,
187             IWearableSensingCallback wearableSensingCallback,
188             RemoteCallback callback) {
189         if (DEBUG) {
190             Slog.i(TAG, "Providing data stream.");
191         }
192         var unused =
193                 post(
194                         service -> {
195                             service.provideDataStream(
196                                     parcelFileDescriptor, wearableSensingCallback, callback);
197                             try {
198                                 // close the local fd after it has been sent to the WSS process
199                                 parcelFileDescriptor.close();
200                             } catch (IOException ex) {
201                                 Slog.w(TAG, "Unable to close the local parcelFileDescriptor.", ex);
202                             }
203                         });
204     }
205 
206     /**
207      * Provides the implementation data.
208      *
209      * @param data Application configuration data to provide to the implementation.
210      * @param sharedMemory The unrestricted data blob to provide to the implementation.
211      * @param callback The callback for service status
212      */
provideData(PersistableBundle data, SharedMemory sharedMemory, RemoteCallback callback)213     public void provideData(PersistableBundle data,
214             SharedMemory sharedMemory,
215             RemoteCallback callback) {
216         if (DEBUG) {
217             Slog.i(TAG, "Providing data.");
218         }
219         post(service -> service.provideData(data, sharedMemory, callback));
220     }
221 
222     /**
223      * Registers a data request observer with WearableSensingService.
224      *
225      * @param dataType The data type to listen to. Values are defined by the application that
226      *     implements WearableSensingService.
227      * @param dataRequestCallback The observer to send data requests to.
228      * @param dataRequestObserverId The unique ID for the data request observer. It will be used for
229      *     unregistering the observer.
230      * @param packageName The package name of the app that will receive the data requests.
231      * @param statusCallback The callback for status of the method call.
232      */
registerDataRequestObserver( int dataType, RemoteCallback dataRequestCallback, int dataRequestObserverId, String packageName, RemoteCallback statusCallback)233     public void registerDataRequestObserver(
234             int dataType,
235             RemoteCallback dataRequestCallback,
236             int dataRequestObserverId,
237             String packageName,
238             RemoteCallback statusCallback) {
239         if (DEBUG) {
240             Slog.i(TAG, "Registering data request observer.");
241         }
242         var unused =
243                 post(
244                         service ->
245                                 service.registerDataRequestObserver(
246                                         dataType,
247                                         dataRequestCallback,
248                                         dataRequestObserverId,
249                                         packageName,
250                                         statusCallback));
251     }
252 
253     /**
254      * Unregisters a previously registered data request observer.
255      *
256      * @param dataType The data type the observer was registered against.
257      * @param dataRequestObserverId The unique ID of the observer to unregister.
258      * @param packageName The package name of the app that will receive requests sent to the
259      *     observer.
260      * @param statusCallback The callback for status of the method call.
261      */
unregisterDataRequestObserver( int dataType, int dataRequestObserverId, String packageName, RemoteCallback statusCallback)262     public void unregisterDataRequestObserver(
263             int dataType,
264             int dataRequestObserverId,
265             String packageName,
266             RemoteCallback statusCallback) {
267         if (DEBUG) {
268             Slog.i(TAG, "Unregistering data request observer.");
269         }
270         var unused =
271                 post(
272                         service ->
273                                 service.unregisterDataRequestObserver(
274                                         dataType,
275                                         dataRequestObserverId,
276                                         packageName,
277                                         statusCallback));
278     }
279 
280     /**
281      * Request the wearable to start hotword recognition.
282      *
283      * @param wearableHotwordCallback The callback to send hotword audio data and format to.
284      * @param statusCallback The callback for service status.
285      */
startHotwordRecognition( RemoteCallback wearableHotwordCallback, RemoteCallback statusCallback)286     public void startHotwordRecognition(
287             RemoteCallback wearableHotwordCallback, RemoteCallback statusCallback) {
288         if (DEBUG) {
289             Slog.i(TAG, "Starting to listen for hotword.");
290         }
291         var unused =
292                 post(
293                         service ->
294                                 service.startHotwordRecognition(
295                                         wearableHotwordCallback, statusCallback));
296     }
297 
298     /**
299      * Request the wearable to stop hotword recognition.
300      *
301      * @param statusCallback The callback for service status.
302      */
stopHotwordRecognition(RemoteCallback statusCallback)303     public void stopHotwordRecognition(RemoteCallback statusCallback) {
304         if (DEBUG) {
305             Slog.i(TAG, "Stopping hotword recognition.");
306         }
307         var unused = post(service -> service.stopHotwordRecognition(statusCallback));
308     }
309 
310     /**
311      * Signals to the {@link WearableSensingService} that hotword audio data is accepted by the
312      * {@link android.service.voice.HotwordDetectionService} as valid hotword.
313      */
onValidatedByHotwordDetectionService()314     public void onValidatedByHotwordDetectionService() {
315         if (DEBUG) {
316             Slog.i(TAG, "Requesting hotword audio data egress.");
317         }
318         var unused = post(service -> service.onValidatedByHotwordDetectionService());
319     }
320 
321     /** Stops the active hotword audio stream from the wearable. */
stopActiveHotwordAudio()322     public void stopActiveHotwordAudio() {
323         if (DEBUG) {
324             Slog.i(TAG, "Stopping hotword audio.");
325         }
326         var unused = post(service -> service.stopActiveHotwordAudio());
327     }
328 
329     private static class SecureWearableConnectionContext {
330         final ParcelFileDescriptor mSecureConnection;
331         final IWearableSensingCallback mWearableSensingCallback;
332         final RemoteCallback mStatusCallback;
333 
SecureWearableConnectionContext( ParcelFileDescriptor secureWearableConnection, IWearableSensingCallback wearableSensingCallback, RemoteCallback statusCallback)334         SecureWearableConnectionContext(
335                 ParcelFileDescriptor secureWearableConnection,
336                 IWearableSensingCallback wearableSensingCallback,
337                 RemoteCallback statusCallback) {
338             mSecureConnection = secureWearableConnection;
339             mWearableSensingCallback = wearableSensingCallback;
340             mStatusCallback = statusCallback;
341         }
342     }
343 }
344