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.service.wearable.WearableSensingService.HOTWORD_AUDIO_STREAM_BUNDLE_KEY;
20 import static android.system.OsConstants.F_GETFL;
21 import static android.system.OsConstants.O_ACCMODE;
22 import static android.system.OsConstants.O_RDONLY;
23 
24 import android.Manifest;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.annotation.UserIdInt;
28 import android.app.AppGlobals;
29 import android.app.ambientcontext.AmbientContextEvent;
30 import android.app.wearable.Flags;
31 import android.app.wearable.IWearableSensingCallback;
32 import android.app.wearable.WearableSensingManager;
33 import android.companion.CompanionDeviceManager;
34 import android.content.ComponentName;
35 import android.content.pm.PackageManager;
36 import android.content.pm.PackageManagerInternal;
37 import android.content.pm.ServiceInfo;
38 import android.os.Binder;
39 import android.os.Bundle;
40 import android.os.ParcelFileDescriptor;
41 import android.os.PersistableBundle;
42 import android.os.RemoteCallback;
43 import android.os.RemoteException;
44 import android.os.SharedMemory;
45 import android.service.voice.HotwordAudioStream;
46 import android.service.voice.VoiceInteractionManagerInternal;
47 import android.service.voice.VoiceInteractionManagerInternal.WearableHotwordDetectionCallback;
48 import android.system.ErrnoException;
49 import android.system.Os;
50 import android.system.OsConstants;
51 import android.util.IndentingPrintWriter;
52 import android.util.Slog;
53 
54 import com.android.internal.annotations.GuardedBy;
55 import com.android.internal.annotations.VisibleForTesting;
56 import com.android.internal.infra.AndroidFuture;
57 import com.android.server.LocalServices;
58 import com.android.server.infra.AbstractPerUserSystemService;
59 
60 import java.io.IOException;
61 import java.io.PrintWriter;
62 import java.util.concurrent.atomic.AtomicReference;
63 
64 /**
65  * Per-user manager service for managing sensing {@link AmbientContextEvent}s on Wearables.
66  */
67 final class WearableSensingManagerPerUserService extends
68         AbstractPerUserSystemService<WearableSensingManagerPerUserService,
69                 WearableSensingManagerService> {
70     private static final String TAG = WearableSensingManagerPerUserService.class.getSimpleName();
71 
72     private final PackageManagerInternal mPackageManagerInternal;
73 
74     @Nullable
75     @VisibleForTesting
76     RemoteWearableSensingService mRemoteService;
77 
78     @Nullable private VoiceInteractionManagerInternal mVoiceInteractionManagerInternal;
79 
80     @GuardedBy("mLock")
81     private ComponentName mComponentName;
82 
83     private final Object mSecureChannelLock = new Object();
84 
85     @GuardedBy("mSecureChannelLock")
86     private WearableSensingSecureChannel mSecureChannel;
87 
WearableSensingManagerPerUserService( @onNull WearableSensingManagerService master, Object lock, @UserIdInt int userId)88     WearableSensingManagerPerUserService(
89             @NonNull WearableSensingManagerService master, Object lock, @UserIdInt int userId) {
90         super(master, lock, userId);
91         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
92     }
93 
notifyStatusCallback(RemoteCallback statusCallback, int statusCode)94     public static void notifyStatusCallback(RemoteCallback statusCallback, int statusCode) {
95         Bundle bundle = new Bundle();
96         bundle.putInt(
97                 WearableSensingManager.STATUS_RESPONSE_BUNDLE_KEY, statusCode);
98         statusCallback.sendResult(bundle);
99     }
100 
destroyLocked()101     void destroyLocked() {
102         Slog.d(TAG, "Trying to cancel the remote request. Reason: Service destroyed.");
103         if (mRemoteService != null) {
104             synchronized (mLock) {
105                 mRemoteService.unbind();
106                 mRemoteService = null;
107             }
108         }
109         synchronized (mSecureChannelLock) {
110             if (mSecureChannel != null) {
111                 mSecureChannel.close();
112             }
113         }
114     }
115 
116     @GuardedBy("mLock")
ensureRemoteServiceInitiated()117     private void ensureRemoteServiceInitiated() {
118         if (mRemoteService == null) {
119             mRemoteService = new RemoteWearableSensingService(
120                     getContext(), mComponentName, getUserId());
121         }
122     }
123 
124     @GuardedBy("mLock")
ensureVoiceInteractionManagerInternalInitiated()125     private boolean ensureVoiceInteractionManagerInternalInitiated() {
126         if (mVoiceInteractionManagerInternal == null) {
127             mVoiceInteractionManagerInternal =
128                     LocalServices.getService(VoiceInteractionManagerInternal.class);
129         }
130         return mVoiceInteractionManagerInternal != null;
131     }
132 
133     /**
134      * get the currently bound component name.
135      */
136     @VisibleForTesting
getComponentName()137     ComponentName getComponentName() {
138         return mComponentName;
139     }
140 
141 
142     /**
143      * Resolves and sets up the service if it had not been done yet. Returns true if the service
144      * is available.
145      */
146     @GuardedBy("mLock")
147     @VisibleForTesting
setUpServiceIfNeeded()148     boolean setUpServiceIfNeeded() {
149         if (mComponentName == null) {
150             mComponentName = updateServiceInfoLocked();
151         }
152         if (mComponentName == null) {
153             return false;
154         }
155 
156         ServiceInfo serviceInfo;
157         try {
158             serviceInfo = AppGlobals.getPackageManager().getServiceInfo(
159                     mComponentName, 0, mUserId);
160         } catch (RemoteException e) {
161             Slog.w(TAG, "RemoteException while setting up service");
162             return false;
163         }
164         return serviceInfo != null;
165     }
166 
167     @Override
newServiceInfoLocked(@onNull ComponentName serviceComponent)168     protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
169             throws PackageManager.NameNotFoundException {
170         ServiceInfo serviceInfo;
171         try {
172             serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
173                     0, mUserId);
174             if (serviceInfo != null) {
175                 final String permission = serviceInfo.permission;
176                 if (!Manifest.permission.BIND_WEARABLE_SENSING_SERVICE.equals(
177                         permission)) {
178                     throw new SecurityException(String.format(
179                             "Service %s requires %s permission. Found %s permission",
180                             serviceInfo.getComponentName(),
181                             Manifest.permission.BIND_WEARABLE_SENSING_SERVICE,
182                             serviceInfo.permission));
183                 }
184             }
185         } catch (RemoteException e) {
186             throw new PackageManager.NameNotFoundException(
187                     "Could not get service for " + serviceComponent);
188         }
189         return serviceInfo;
190     }
191 
192     @Override
dumpLocked(@onNull String prefix, @NonNull PrintWriter pw)193     protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
194         synchronized (super.mLock) {
195             super.dumpLocked(prefix, pw);
196         }
197         if (mRemoteService != null) {
198             mRemoteService.dump("", new IndentingPrintWriter(pw, "  "));
199         }
200     }
201 
202     /**
203      * Creates a CompanionDeviceManager secure channel and sends a proxy to the wearable sensing
204      * service.
205      */
onProvideConnection( ParcelFileDescriptor wearableConnection, IWearableSensingCallback wearableSensingCallback, RemoteCallback statusCallback)206     public void onProvideConnection(
207             ParcelFileDescriptor wearableConnection,
208             IWearableSensingCallback wearableSensingCallback,
209             RemoteCallback statusCallback) {
210         Slog.i(TAG, "onProvideConnection in per user service.");
211         final IWearableSensingCallback wrappedWearableSensingCallback;
212         synchronized (mLock) {
213             if (!setUpServiceIfNeeded()) {
214                 Slog.w(TAG, "Detection service is not available at this moment.");
215                 notifyStatusCallback(
216                         statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
217                 return;
218             }
219             wrappedWearableSensingCallback = wrapWearableSensingCallback(wearableSensingCallback);
220         }
221         synchronized (mSecureChannelLock) {
222             if (mSecureChannel != null) {
223                 mSecureChannel.close();
224             }
225             try {
226                 final AtomicReference<WearableSensingSecureChannel> currentSecureChannelRef =
227                         new AtomicReference<>();
228                 mSecureChannel =
229                         WearableSensingSecureChannel.create(
230                                 getContext().getSystemService(CompanionDeviceManager.class),
231                                 wearableConnection,
232                                 new WearableSensingSecureChannel.SecureTransportListener() {
233                                     @Override
234                                     public void onSecureTransportAvailable(
235                                             ParcelFileDescriptor secureTransport) {
236                                         Slog.i(TAG, "calling over to remote service.");
237                                         synchronized (mLock) {
238                                             ensureRemoteServiceInitiated();
239                                             mRemoteService.provideSecureConnection(
240                                                     secureTransport,
241                                                     wrappedWearableSensingCallback,
242                                                     statusCallback);
243                                         }
244                                     }
245 
246                                     @Override
247                                     public void onError() {
248                                         if (Flags.enableRestartWssProcess()) {
249                                             synchronized (mSecureChannelLock) {
250                                                 if (mSecureChannel != null
251                                                         && mSecureChannel
252                                                                 == currentSecureChannelRef.get()) {
253                                                     mRemoteService
254                                                             .killWearableSensingServiceProcess();
255                                                     mSecureChannel = null;
256                                                 }
257                                             }
258                                         }
259                                         if (Flags.enableProvideWearableConnectionApi()) {
260                                             notifyStatusCallback(
261                                                     statusCallback,
262                                                     WearableSensingManager.STATUS_CHANNEL_ERROR);
263                                         }
264                                     }
265                                 });
266                 currentSecureChannelRef.set(mSecureChannel);
267             } catch (IOException ex) {
268                 Slog.e(TAG, "Unable to create the secure channel.", ex);
269                 if (Flags.enableProvideWearableConnectionApi()) {
270                     notifyStatusCallback(
271                             statusCallback, WearableSensingManager.STATUS_CHANNEL_ERROR);
272                 }
273             }
274         }
275     }
276 
277     /**
278      * Handles sending the provided data stream for the wearable to the wearable sensing service.
279      */
onProvideDataStream( ParcelFileDescriptor parcelFileDescriptor, @Nullable IWearableSensingCallback wearableSensingCallback, RemoteCallback statusCallback)280     public void onProvideDataStream(
281             ParcelFileDescriptor parcelFileDescriptor,
282             @Nullable IWearableSensingCallback wearableSensingCallback,
283             RemoteCallback statusCallback) {
284         Slog.i(
285                 TAG,
286                 "onProvideDataStream in per user service. Is data stream read-only? "
287                         + isReadOnly(parcelFileDescriptor));
288         synchronized (mLock) {
289             if (!setUpServiceIfNeeded()) {
290                 Slog.w(TAG, "Detection service is not available at this moment.");
291                 notifyStatusCallback(
292                         statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
293                 return;
294             }
295             Slog.i(TAG, "calling over to remote servvice.");
296             ensureRemoteServiceInitiated();
297             mRemoteService.provideDataStream(
298                     parcelFileDescriptor,
299                     wrapWearableSensingCallback(wearableSensingCallback),
300                     statusCallback);
301         }
302     }
303 
304     /**
305      * Handles sending the provided data to the wearable sensing service.
306      */
onProvidedData(PersistableBundle data, SharedMemory sharedMemory, RemoteCallback callback)307     public void onProvidedData(PersistableBundle data,
308             SharedMemory sharedMemory,
309             RemoteCallback callback) {
310         synchronized (mLock) {
311             if (!setUpServiceIfNeeded()) {
312                 Slog.w(TAG, "Detection service is not available at this moment.");
313                 notifyStatusCallback(callback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
314                 return;
315             }
316             ensureRemoteServiceInitiated();
317             if (sharedMemory != null) {
318                 sharedMemory.setProtect(OsConstants.PROT_READ);
319             }
320             mRemoteService.provideData(data, sharedMemory, callback);
321         }
322     }
323 
324     /**
325      * Handles registering a data request observer.
326      *
327      * @param dataType The data type to listen to. Values are defined by the application that
328      *     implements WearableSensingService.
329      * @param dataRequestObserver The observer to register.
330      * @param dataRequestObserverId The unique ID for the data request observer. It will be used for
331      *     unregistering the observer.
332      * @param packageName The package name of the app that will receive the data requests.
333      * @param statusCallback The callback for status of the method call.
334      */
onRegisterDataRequestObserver( int dataType, RemoteCallback dataRequestObserver, int dataRequestObserverId, String packageName, RemoteCallback statusCallback)335     public void onRegisterDataRequestObserver(
336             int dataType,
337             RemoteCallback dataRequestObserver,
338             int dataRequestObserverId,
339             String packageName,
340             RemoteCallback statusCallback) {
341         synchronized (mLock) {
342             if (!setUpServiceIfNeeded()) {
343                 Slog.w(TAG, "Detection service is not available at this moment.");
344                 notifyStatusCallback(
345                         statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
346                 return;
347             }
348             ensureRemoteServiceInitiated();
349             mRemoteService.registerDataRequestObserver(
350                     dataType,
351                     dataRequestObserver,
352                     dataRequestObserverId,
353                     packageName,
354                     statusCallback);
355         }
356     }
357 
358     /**
359      * Handles unregistering a previously registered data request observer.
360      *
361      * @param dataType The data type the observer was registered against.
362      * @param dataRequestObserverId The unique ID of the observer to unregister.
363      * @param packageName The package name of the app that will receive requests sent to the
364      *     observer.
365      * @param statusCallback The callback for status of the method call.
366      */
onUnregisterDataRequestObserver( int dataType, int dataRequestObserverId, String packageName, RemoteCallback statusCallback)367     public void onUnregisterDataRequestObserver(
368             int dataType,
369             int dataRequestObserverId,
370             String packageName,
371             RemoteCallback statusCallback) {
372         synchronized (mLock) {
373             if (!setUpServiceIfNeeded()) {
374                 Slog.w(TAG, "Detection service is not available at this moment.");
375                 notifyStatusCallback(
376                         statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
377                 return;
378             }
379             ensureRemoteServiceInitiated();
380             mRemoteService.unregisterDataRequestObserver(
381                     dataType, dataRequestObserverId, packageName, statusCallback);
382         }
383     }
384 
385     /** Handles starting hotword listening. */
onStartHotwordRecognition( ComponentName targetVisComponentName, RemoteCallback statusCallback)386     public void onStartHotwordRecognition(
387             ComponentName targetVisComponentName, RemoteCallback statusCallback) {
388         synchronized (mLock) {
389             if (!setUpServiceIfNeeded()) {
390                 Slog.w(TAG, "Detection service is not available at this moment.");
391                 notifyStatusCallback(
392                         statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
393                 return;
394             }
395             if (!ensureVoiceInteractionManagerInternalInitiated()) {
396                 Slog.w(TAG, "Voice interaction manager is not available at this moment.");
397                 notifyStatusCallback(
398                         statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
399                 return;
400             }
401             ensureRemoteServiceInitiated();
402             mRemoteService.startHotwordRecognition(
403                     createWearableHotwordCallback(targetVisComponentName), statusCallback);
404         }
405     }
406 
407     /** Handles stopping hotword listening. */
onStopHotwordRecognition(RemoteCallback statusCallback)408     public void onStopHotwordRecognition(RemoteCallback statusCallback) {
409         synchronized (mLock) {
410             if (!setUpServiceIfNeeded()) {
411                 Slog.w(TAG, "Detection service is not available at this moment.");
412                 notifyStatusCallback(
413                         statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
414                 return;
415             }
416             ensureRemoteServiceInitiated();
417             mRemoteService.stopHotwordRecognition(statusCallback);
418         }
419     }
420 
onValidatedByHotwordDetectionService()421     private void onValidatedByHotwordDetectionService() {
422         synchronized (mLock) {
423             if (!setUpServiceIfNeeded()) {
424                 Slog.w(TAG, "Wearable sensing service is not available at this moment.");
425                 return;
426             }
427             ensureRemoteServiceInitiated();
428             mRemoteService.onValidatedByHotwordDetectionService();
429         }
430     }
431 
stopActiveHotwordAudio()432     private void stopActiveHotwordAudio() {
433         synchronized (mLock) {
434             if (!setUpServiceIfNeeded()) {
435                 Slog.w(TAG, "Wearable sensing service is not available at this moment.");
436                 return;
437             }
438             ensureRemoteServiceInitiated();
439             mRemoteService.stopActiveHotwordAudio();
440         }
441     }
442 
createWearableHotwordCallback(ComponentName targetVisComponentName)443     private RemoteCallback createWearableHotwordCallback(ComponentName targetVisComponentName) {
444         return new RemoteCallback(
445                 result -> {
446                     HotwordAudioStream hotwordAudioStream =
447                             result.getParcelable(
448                                     HOTWORD_AUDIO_STREAM_BUNDLE_KEY, HotwordAudioStream.class);
449                     if (hotwordAudioStream == null) {
450                         Slog.w(TAG, "No hotword audio stream received, unable to process hotword.");
451                         return;
452                     }
453                     final long identity = Binder.clearCallingIdentity();
454                     try {
455                         mVoiceInteractionManagerInternal.startListeningFromWearable(
456                                 hotwordAudioStream.getAudioStreamParcelFileDescriptor(),
457                                 hotwordAudioStream.getAudioFormat(),
458                                 hotwordAudioStream.getMetadata(),
459                                 targetVisComponentName,
460                                 getUserId(),
461                                 createHotwordDetectionCallback());
462                     } finally {
463                         Binder.restoreCallingIdentity(identity);
464                     }
465                 });
466     }
467 
createHotwordDetectionCallback()468     private WearableHotwordDetectionCallback createHotwordDetectionCallback() {
469         return new WearableHotwordDetectionCallback() {
470             @Override
471             public void onDetected() {
472                 Slog.i(TAG, "hotwordDetectionCallback onDetected.");
473                 onValidatedByHotwordDetectionService();
474             }
475 
476             @Override
477             public void onRejected() {
478                 Slog.i(TAG, "hotwordDetectionCallback onRejected.");
479                 stopActiveHotwordAudio();
480             }
481 
482             @Override
483             public void onError(String errorMessage) {
484                 Slog.i(TAG, "hotwordDetectionCallback onError. ErrorMessage: " + errorMessage);
485                 stopActiveHotwordAudio();
486             }
487         };
488     }
489 
490     @GuardedBy("mLock")
491     private @Nullable IWearableSensingCallback wrapWearableSensingCallback(
492             IWearableSensingCallback callbackFromAppProcess) {
493         if (callbackFromAppProcess == null) {
494             return null;
495         }
496         if (mComponentName == null) {
497             Slog.w(TAG, "Cannot create WearableSensingCallback because mComponentName is null.");
498             return null;
499         }
500         if (Binder.getCallingUid()
501                 != mPackageManagerInternal.getPackageUid(
502                         mComponentName.getPackageName(), /* flags= */ 0, mUserId)) {
503             Slog.d(
504                     TAG,
505                     "Caller does not belong to the package that provides the WearableSensingService"
506                             + " implementation. Do not forward WearableSensingCallback to"
507                             + " WearableSensingService.");
508             return null;
509         }
510         return new IWearableSensingCallback.Stub() {
511             @Override
512             public void openFile(
513                     String filename,
514                     AndroidFuture<ParcelFileDescriptor> futureFromWearableSensingService)
515                     throws RemoteException {
516                 AndroidFuture<ParcelFileDescriptor> futureFromSystemServer =
517                         new AndroidFuture<ParcelFileDescriptor>()
518                                 .whenComplete(
519                                         (pfdFromApp, throwable) -> {
520                                             if (throwable != null) {
521                                                 Slog.e(
522                                                         TAG,
523                                                         "Error when reading file " + filename,
524                                                         throwable);
525                                                 futureFromWearableSensingService.complete(null);
526                                                 return;
527                                             }
528                                             if (pfdFromApp == null) {
529                                                 futureFromWearableSensingService.complete(null);
530                                                 return;
531                                             }
532                                             if (isReadOnly(pfdFromApp)) {
533                                                 futureFromWearableSensingService.complete(
534                                                         pfdFromApp);
535                                             } else {
536                                                 Slog.w(
537                                                         TAG,
538                                                         "Received writable ParcelFileDescriptor"
539                                                             + " from app process. To prevent"
540                                                             + " arbitrary data egress, sending null"
541                                                             + " to WearableSensingService"
542                                                             + " instead.");
543                                                 futureFromWearableSensingService.complete(null);
544                                             }
545                                         });
546                 callbackFromAppProcess.openFile(filename, futureFromSystemServer);
547             }
548         };
549     }
550 
551     private static boolean isReadOnly(ParcelFileDescriptor parcelFileDescriptor) {
552         try {
553             int readMode =
554                     Os.fcntlInt(parcelFileDescriptor.getFileDescriptor(), F_GETFL, 0) & O_ACCMODE;
555             return readMode == O_RDONLY;
556         } catch (ErrnoException ex) {
557             Slog.w(
558                     TAG,
559                     "Error encountered when trying to determine if the parcelFileDescriptor is"
560                         + " read-only. Treating it as not read-only",
561                     ex);
562         }
563         return false;
564     }
565 }
566