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