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.app.wearable; 18 19 import android.Manifest; 20 import android.annotation.CallbackExecutor; 21 import android.annotation.FlaggedApi; 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.RequiresPermission; 26 import android.annotation.SystemApi; 27 import android.annotation.SystemService; 28 import android.app.PendingIntent; 29 import android.app.ambientcontext.AmbientContextEvent; 30 import android.app.compat.CompatChanges; 31 import android.companion.CompanionDeviceManager; 32 import android.compat.annotation.ChangeId; 33 import android.compat.annotation.EnabledSince; 34 import android.content.ComponentName; 35 import android.content.Context; 36 import android.content.Intent; 37 import android.os.Binder; 38 import android.os.Build; 39 import android.os.ParcelFileDescriptor; 40 import android.os.PersistableBundle; 41 import android.os.RemoteCallback; 42 import android.os.RemoteException; 43 import android.os.SharedMemory; 44 import android.service.wearable.WearableSensingService; 45 import android.system.OsConstants; 46 import android.util.Slog; 47 48 import com.android.internal.infra.AndroidFuture; 49 50 import java.io.File; 51 import java.io.FileNotFoundException; 52 import java.io.IOException; 53 import java.io.InputStream; 54 import java.io.OutputStream; 55 import java.lang.annotation.Retention; 56 import java.lang.annotation.RetentionPolicy; 57 import java.util.concurrent.Executor; 58 import java.util.function.Consumer; 59 60 /** 61 * Allows granted apps to manage the WearableSensingService. 62 * Applications are responsible for managing the connection to Wearables. Applications can choose 63 * to provide a data stream to the WearableSensingService to use for 64 * computing {@link AmbientContextEvent}s. Applications can also optionally provide their own 65 * defined data to power the detection of {@link AmbientContextEvent}s. 66 * Methods on this class requires the caller to hold and be granted the 67 * {@link Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE}. 68 * 69 * <p>The use of "Wearable" here is not the same as the Android Wear platform and should be treated 70 * separately. </p> 71 * 72 * @hide 73 */ 74 @SystemApi 75 @SystemService(Context.WEARABLE_SENSING_SERVICE) 76 public class WearableSensingManager { 77 /** 78 * The bundle key for the service status query result, used in 79 * {@code RemoteCallback#sendResult}. 80 * 81 * @hide 82 */ 83 public static final String STATUS_RESPONSE_BUNDLE_KEY = 84 "android.app.wearable.WearableSensingStatusBundleKey"; 85 86 /** 87 * The Intent extra key for the data request in the Intent sent to the PendingIntent registered 88 * with {@link #registerDataRequestObserver(int, PendingIntent, Executor, Consumer)}. 89 * 90 * @hide 91 */ 92 public static final String EXTRA_WEARABLE_SENSING_DATA_REQUEST = 93 "android.app.wearable.extra.WEARABLE_SENSING_DATA_REQUEST"; 94 95 /** 96 * An unknown status. 97 */ 98 public static final int STATUS_UNKNOWN = 0; 99 100 /** 101 * The value of the status code that indicates success. 102 */ 103 public static final int STATUS_SUCCESS = 1; 104 105 /** 106 * The value of the status code that indicates one or more of the requested events are not 107 * supported. 108 * 109 * @deprecated WearableSensingManager does not deal with events. Use {@link 110 * STATUS_UNSUPPORTED_OPERATION} instead for operations not supported by the implementation of 111 * {@link WearableSensingService}. 112 */ 113 @Deprecated 114 public static final int STATUS_UNSUPPORTED = 2; 115 116 /** 117 * The value of the status code that indicates service not available. 118 */ 119 public static final int STATUS_SERVICE_UNAVAILABLE = 3; 120 121 /** 122 * The value of the status code that there's no connection to the wearable. 123 */ 124 public static final int STATUS_WEARABLE_UNAVAILABLE = 4; 125 126 /** 127 * The value of the status code that the app is not granted access. 128 */ 129 public static final int STATUS_ACCESS_DENIED = 5; 130 131 /** 132 * The value of the status code that indicates the method called is not supported by the 133 * implementation of {@link WearableSensingService}. 134 */ 135 @FlaggedApi(Flags.FLAG_ENABLE_UNSUPPORTED_OPERATION_STATUS_CODE) 136 public static final int STATUS_UNSUPPORTED_OPERATION = 6; 137 138 /** 139 * The value of the status code that indicates an error occurred in the encrypted channel backed 140 * by the provided connection. See {@link #provideConnection(ParcelFileDescriptor, 141 * Executor, Consumer)}. 142 */ 143 @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API) 144 public static final int STATUS_CHANNEL_ERROR = 7; 145 146 /** The value of the status code that indicates the provided data type is not supported. */ 147 @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) 148 public static final int STATUS_UNSUPPORTED_DATA_TYPE = 8; 149 150 /** @hide */ 151 @IntDef( 152 prefix = {"STATUS_"}, 153 value = { 154 STATUS_UNKNOWN, 155 STATUS_SUCCESS, 156 STATUS_UNSUPPORTED, 157 STATUS_SERVICE_UNAVAILABLE, 158 STATUS_WEARABLE_UNAVAILABLE, 159 STATUS_ACCESS_DENIED, 160 STATUS_UNSUPPORTED_OPERATION, 161 STATUS_CHANNEL_ERROR, 162 STATUS_UNSUPPORTED_DATA_TYPE 163 }) 164 @Retention(RetentionPolicy.SOURCE) 165 public @interface StatusCode {} 166 167 /** 168 * If the WearableSensingService implementation belongs to the same APK as the caller, calling 169 * {@link #provideDataStream(ParcelFileDescriptor, Executor, Consumer)} will allow 170 * WearableSensingService to read from the caller's file directory via {@link 171 * Context#openFileInput(String)}. The read will be proxied via the caller's process and 172 * executed by the {@code executor} provided to this method. 173 */ 174 @ChangeId 175 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM) 176 static final long ALLOW_WEARABLE_SENSING_SERVICE_FILE_READ = 330701114L; 177 178 /** 179 * Retrieves a {@link WearableSensingDataRequest} from the Intent sent to the PendingIntent 180 * provided to {@link #registerDataRequestObserver(int, PendingIntent, Executor, Consumer)}. 181 * 182 * @param intent The Intent received from the PendingIntent. 183 * @return The WearableSensingDataRequest in the provided Intent, or null if the Intent does not 184 * contain a WearableSensingDataRequest. 185 */ 186 @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) 187 @Nullable getDataRequestFromIntent(@onNull Intent intent)188 public static WearableSensingDataRequest getDataRequestFromIntent(@NonNull Intent intent) { 189 return intent.getParcelableExtra( 190 EXTRA_WEARABLE_SENSING_DATA_REQUEST, WearableSensingDataRequest.class); 191 } 192 193 private static final String TAG = WearableSensingManager.class.getSimpleName(); 194 private final Context mContext; 195 private final IWearableSensingManager mService; 196 197 /** 198 * {@hide} 199 */ WearableSensingManager(Context context, IWearableSensingManager service)200 public WearableSensingManager(Context context, IWearableSensingManager service) { 201 mContext = context; 202 mService = service; 203 } 204 205 /** 206 * Provides a remote wearable device connection to the WearableSensingService and sends the 207 * resulting status to the {@code statusConsumer} after the call. 208 * 209 * <p>This is used by applications that will also provide an implementation of the isolated 210 * WearableSensingService. 211 * 212 * <p>The provided {@code wearableConnection} is expected to be a connection to a remotely 213 * connected wearable device. This {@code wearableConnection} will be attached to 214 * CompanionDeviceManager via {@link CompanionDeviceManager#attachSystemDataTransport(int, 215 * InputStream, OutputStream)}, which will create an encrypted channel using {@code 216 * wearableConnection} as the raw underlying connection. The wearable device is expected to 217 * attach its side of the raw connection to its CompanionDeviceManager via the same method so 218 * that the two CompanionDeviceManagers on the two devices can perform attestation and set up 219 * the encrypted channel. Attestation requirements are listed in 220 * com.android.server.security.AttestationVerificationPeerDeviceVerifier 221 * 222 * <p>A proxy to the encrypted channel will be provided to the WearableSensingService, which is 223 * referred to as the secureWearableConnection in WearableSensingService. Any data written to 224 * secureWearableConnection will be encrypted by CompanionDeviceManager and sent over the raw 225 * {@code wearableConnection} to the remote wearable device, which is expected to use its 226 * CompanionDeviceManager to decrypt the data. Encrypted data arriving at the raw {@code 227 * wearableConnection} will be decrypted by CompanionDeviceManager and be readable as plain text 228 * from secureWearableConnection. The raw {@code wearableConnection} provided to this method 229 * will not be directly available to the WearableSensingService. 230 * 231 * <p>If an error occurred in the encrypted channel (such as the underlying stream closed), the 232 * system will send a status code of {@link STATUS_CHANNEL_ERROR} to the {@code statusConsumer} 233 * and kill the WearableSensingService process. 234 * 235 * <p>Before providing the secureWearableConnection, the system will restart the 236 * WearableSensingService process if it has not been restarted since the last 237 * secureWearableConnection was provided. Other method calls into WearableSensingService may be 238 * dropped during the restart. The caller is responsible for ensuring other method calls are 239 * queued until a success status is returned from the {@code statusConsumer}. 240 * 241 * <p>If the WearableSensingService implementation belongs to the same APK as the caller, 242 * calling this method will allow WearableSensingService to read from the caller's file 243 * directory via {@link Context#openFileInput(String)}. The read will be proxied via the 244 * caller's process and executed by the {@code executor} provided to this method. 245 * 246 * @param wearableConnection The connection to provide 247 * @param executor Executor on which to run the consumer callback 248 * @param statusConsumer A consumer that handles the status codes for providing the connection 249 * and errors in the encrypted channel. 250 */ 251 @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) 252 @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API) provideConnection( @onNull ParcelFileDescriptor wearableConnection, @NonNull @CallbackExecutor Executor executor, @NonNull @StatusCode Consumer<Integer> statusConsumer)253 public void provideConnection( 254 @NonNull ParcelFileDescriptor wearableConnection, 255 @NonNull @CallbackExecutor Executor executor, 256 @NonNull @StatusCode Consumer<Integer> statusConsumer) { 257 RemoteCallback statusCallback = createStatusCallback(executor, statusConsumer); 258 try { 259 // The wearableSensingCallback is included in this method call even though it is not 260 // semantically related to the connection because we want to avoid race conditions 261 // during the process restart triggered by this method call. See 262 // com.android.server.wearable.RemoteWearableSensingService for details. 263 mService.provideConnection( 264 wearableConnection, createWearableSensingCallback(executor), statusCallback); 265 } catch (RemoteException e) { 266 throw e.rethrowFromSystemServer(); 267 } 268 } 269 270 /** 271 * Provides a data stream to the WearableSensingService that's backed by the 272 * parcelFileDescriptor, and sends the result to the {@link Consumer} right after the call. This 273 * is used by applications that will also provide an implementation of an isolated 274 * WearableSensingService. If the data stream was provided successfully {@link 275 * WearableSensingManager#STATUS_SUCCESS} will be provided. 276 * 277 * <p>Starting from target SDK level 35, if the WearableSensingService implementation belongs to 278 * the same APK as the caller, calling this method will allow WearableSensingService to read 279 * from the caller's file directory via {@link Context#openFileInput(String)}. The read will be 280 * proxied via the caller's process and executed by the {@code executor} provided to this 281 * method. 282 * 283 * @param parcelFileDescriptor The data stream to provide 284 * @param executor Executor on which to run the consumer callback 285 * @param statusConsumer A consumer that handles the status codes, which is returned right after 286 * the call. 287 * @deprecated Use {@link #provideConnection(ParcelFileDescriptor, Executor, Consumer)} instead 288 * to provide a remote wearable device connection to the WearableSensingService 289 */ 290 @Deprecated 291 @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) provideDataStream( @onNull ParcelFileDescriptor parcelFileDescriptor, @NonNull @CallbackExecutor Executor executor, @NonNull @StatusCode Consumer<Integer> statusConsumer)292 public void provideDataStream( 293 @NonNull ParcelFileDescriptor parcelFileDescriptor, 294 @NonNull @CallbackExecutor Executor executor, 295 @NonNull @StatusCode Consumer<Integer> statusConsumer) { 296 RemoteCallback statusCallback = createStatusCallback(executor, statusConsumer); 297 IWearableSensingCallback wearableSensingCallback = null; 298 if (CompatChanges.isChangeEnabled(ALLOW_WEARABLE_SENSING_SERVICE_FILE_READ)) { 299 wearableSensingCallback = createWearableSensingCallback(executor); 300 } 301 try { 302 mService.provideDataStream( 303 parcelFileDescriptor, wearableSensingCallback, statusCallback); 304 } catch (RemoteException e) { 305 throw e.rethrowFromSystemServer(); 306 } 307 } 308 309 /** 310 * Sets configuration and provides read-only data in a {@link PersistableBundle} that may be 311 * used by the WearableSensingService, and sends the result to the {@link Consumer} 312 * right after the call. It is dependent on the application to 313 * define the type of data to provide. This is used by applications that will also 314 * provide an implementation of an isolated WearableSensingService. If the data was 315 * provided successfully {@link WearableSensingManager#STATUS_SUCCESS} will be povided. 316 * 317 * @param data Application configuration data to provide to the {@link WearableSensingService}. 318 * PersistableBundle does not allow any remotable objects or other contents 319 * that can be used to communicate with other processes. 320 * @param sharedMemory The unrestricted data blob to 321 * provide to the {@link WearableSensingService}. Use this to provide the 322 * sensing models data or other such data to the trusted process. 323 * The sharedMemory must be read only and protected with 324 * {@link OsConstants.PROT_READ}. 325 * Other operations will be removed by the system. 326 * @param executor Executor on which to run the consumer callback 327 * @param statusConsumer A consumer that handles the status codes, which is returned 328 * right after the call 329 */ 330 @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) provideData( @onNull PersistableBundle data, @Nullable SharedMemory sharedMemory, @NonNull @CallbackExecutor Executor executor, @NonNull @StatusCode Consumer<Integer> statusConsumer)331 public void provideData( 332 @NonNull PersistableBundle data, @Nullable SharedMemory sharedMemory, 333 @NonNull @CallbackExecutor Executor executor, 334 @NonNull @StatusCode Consumer<Integer> statusConsumer) { 335 try { 336 RemoteCallback callback = createStatusCallback(executor, statusConsumer); 337 mService.provideData(data, sharedMemory, callback); 338 } catch (RemoteException e) { 339 throw e.rethrowFromSystemServer(); 340 } 341 } 342 343 /** 344 * Registers a data request observer for the provided data type. 345 * 346 * <p>When data is requested, the provided {@code dataRequestPendingIntent} will be invoked. A 347 * {@link WearableSensingDataRequest} can be extracted from the Intent sent to {@code 348 * dataRequestPendingIntent} by calling {@link #getDataRequestFromIntent(Intent)}. The observer 349 * can then provide the requested data via {@link #provideData(PersistableBundle, SharedMemory, 350 * Executor, Consumer)}. 351 * 352 * <p>There is no limit to the number of observers registered for a data type. How they are 353 * handled depends on the implementation of WearableSensingService. 354 * 355 * <p>When the observer is no longer needed, {@link #unregisterDataRequestObserver(int, 356 * PendingIntent, Executor, Consumer)} should be called with the same {@code 357 * dataRequestPendingIntent}. It should be done regardless of the status code returned from 358 * {@code statusConsumer} in order to clean up housekeeping data for the {@code 359 * dataRequestPendingIntent} maintained by the system. 360 * 361 * <p>Example: 362 * 363 * <pre>{@code 364 * // Create a PendingIntent for MyDataRequestBroadcastReceiver 365 * Intent intent = 366 * new Intent(actionString).setClass(context, MyDataRequestBroadcastReceiver.class); 367 * PendingIntent pendingIntent = PendingIntent.getBroadcast( 368 * context, 0, intent, PendingIntent.FLAG_MUTABLE); 369 * 370 * // Register the PendingIntent as a data request observer 371 * wearableSensingManager.registerDataRequestObserver( 372 * dataType, pendingIntent, executor, statusConsumer); 373 * 374 * // Within MyDataRequestBroadcastReceiver, receive the broadcast Intent and extract the 375 * // WearableSensingDataRequest 376 * {@literal @}Override 377 * public void onReceive(Context context, Intent intent) { 378 * WearableSensingDataRequest dataRequest = 379 * WearableSensingManager.getDataRequestFromIntent(intent); 380 * // After parsing the dataRequest, provide the data 381 * wearableSensingManager.provideData(data, sharedMemory, executor, statusConsumer); 382 * } 383 * }</pre> 384 * 385 * @param dataType The data type to listen to. Values are defined by the application that 386 * implements {@link WearableSensingService}. 387 * @param dataRequestPendingIntent A mutable {@link PendingIntent} that will be invoked when 388 * data is requested. See {@link #getDataRequestFromIntent(Intent)}. Activities are not 389 * allowed to be launched using this PendingIntent. 390 * @param executor Executor on which to run the consumer callback. 391 * @param statusConsumer A consumer that handles the status code for the observer registration. 392 */ 393 @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) 394 @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) registerDataRequestObserver( int dataType, @NonNull PendingIntent dataRequestPendingIntent, @NonNull @CallbackExecutor Executor executor, @NonNull @StatusCode Consumer<Integer> statusConsumer)395 public void registerDataRequestObserver( 396 int dataType, 397 @NonNull PendingIntent dataRequestPendingIntent, 398 @NonNull @CallbackExecutor Executor executor, 399 @NonNull @StatusCode Consumer<Integer> statusConsumer) { 400 try { 401 RemoteCallback statusCallback = createStatusCallback(executor, statusConsumer); 402 mService.registerDataRequestObserver( 403 dataType, dataRequestPendingIntent, statusCallback); 404 } catch (RemoteException e) { 405 throw e.rethrowFromSystemServer(); 406 } 407 } 408 409 /** 410 * Unregisters a previously registered data request observer. If the provided {@link 411 * PendingIntent} was not registered, or is already unregistered, the {@link 412 * WearableSensingService} will not be notified. 413 * 414 * @param dataType The data type the observer is for. 415 * @param dataRequestPendingIntent The observer to unregister. 416 * @param executor Executor on which to run the consumer callback. 417 * @param statusConsumer A consumer that handles the status code for the observer 418 * unregistration. 419 */ 420 @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) 421 @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) unregisterDataRequestObserver( int dataType, @NonNull PendingIntent dataRequestPendingIntent, @NonNull @CallbackExecutor Executor executor, @NonNull @StatusCode Consumer<Integer> statusConsumer)422 public void unregisterDataRequestObserver( 423 int dataType, 424 @NonNull PendingIntent dataRequestPendingIntent, 425 @NonNull @CallbackExecutor Executor executor, 426 @NonNull @StatusCode Consumer<Integer> statusConsumer) { 427 try { 428 RemoteCallback statusCallback = createStatusCallback(executor, statusConsumer); 429 mService.unregisterDataRequestObserver( 430 dataType, dataRequestPendingIntent, statusCallback); 431 } catch (RemoteException e) { 432 throw e.rethrowFromSystemServer(); 433 } 434 } 435 436 /** 437 * Requests the wearable to start hotword recognition. 438 * 439 * <p>When this method is called, the system will attempt to provide a {@code 440 * Consumer<android.service.voice.HotwordAudioStream>} to {@link WearableSensingService}. After 441 * first-stage hotword is detected on a wearable, {@link WearableSensingService} should send the 442 * hotword audio to the {@code Consumer<android.service.voice.HotwordAudioStream>}, which will 443 * forward the data to the {@link android.service.voice.HotwordDetectionService} for 444 * second-stage hotword validation. If hotword is detected there, the audio data will be 445 * forwarded to the {@link android.service.voice.VoiceInteractionService}. 446 * 447 * <p>If the {@code targetVisComponentName} provided here is not null, when {@link 448 * WearableSensingService} sends hotword audio to the {@code 449 * Consumer<android.service.voice.HotwordAudioStream>}, the system will check whether the {@link 450 * android.service.voice.VoiceInteractionService} at that time is {@code 451 * targetVisComponentName}. If not, the system will call {@link 452 * WearableSensingService#onStopHotwordAudioStream()} and will not forward the audio 453 * data to the current {@link android.service.voice.HotwordDetectionService} nor {@link 454 * android.service.voice.VoiceInteractionService}. The system will not send a status code to 455 * {@code statusConsumer} regarding the {@code targetVisComponentName} check. The caller is 456 * responsible for determining whether the system's {@link 457 * android.service.voice.VoiceInteractionService} is the same as {@code targetVisComponentName}. 458 * The check here is just a protection against race conditions. 459 * 460 * <p>Calling this method again will send a new {@code 461 * Consumer<android.service.voice.HotwordAudioStream>} to {@link WearableSensingService}. For 462 * audio data sent to the new consumer, the system will perform the above check using the newly 463 * provided {@code targetVisComponentName}. The {@link WearableSensingService} should not 464 * continue to use the previous consumers after receiving a new one. 465 * 466 * <p>If the {@code statusConsumer} returns {@link STATUS_SUCCESS}, the caller should call 467 * {@link #stopHotwordRecognition(Executor, Consumer)} when it wants the wearable to stop 468 * listening for hotword. If the {@code statusConsumer} returns any other status code, a failure 469 * has occurred and calling {@link #stopHotwordRecognition(Executor, Consumer)} is not 470 * required. The system will not retry listening automatically. The caller should call this 471 * method again if they want to retry. 472 * 473 * <p>If a failure occurred after the {@link statusConsumer} returns {@link STATUS_SUCCESS}, 474 * {@link statusConsumer} will be invoked again with a status code other than {@link 475 * STATUS_SUCCESS}. 476 * 477 * @param targetVisComponentName The ComponentName of the target VoiceInteractionService. 478 * @param executor Executor on which to run the consumer callback. 479 * @param statusConsumer A consumer that handles the status codes. 480 */ 481 @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API) 482 @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) startHotwordRecognition( @ullable ComponentName targetVisComponentName, @NonNull @CallbackExecutor Executor executor, @NonNull @StatusCode Consumer<Integer> statusConsumer)483 public void startHotwordRecognition( 484 @Nullable ComponentName targetVisComponentName, 485 @NonNull @CallbackExecutor Executor executor, 486 @NonNull @StatusCode Consumer<Integer> statusConsumer) { 487 try { 488 mService.startHotwordRecognition( 489 targetVisComponentName, createStatusCallback(executor, statusConsumer)); 490 } catch (RemoteException e) { 491 throw e.rethrowFromSystemServer(); 492 } 493 } 494 495 /** 496 * Requests the wearable to stop hotword recognition. 497 * 498 * @param executor Executor on which to run the consumer callback. 499 * @param statusConsumer A consumer that handles the status codes. 500 */ 501 @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API) 502 @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) stopHotwordRecognition( @onNull @allbackExecutor Executor executor, @NonNull @StatusCode Consumer<Integer> statusConsumer)503 public void stopHotwordRecognition( 504 @NonNull @CallbackExecutor Executor executor, 505 @NonNull @StatusCode Consumer<Integer> statusConsumer) { 506 try { 507 mService.stopHotwordRecognition(createStatusCallback(executor, statusConsumer)); 508 } catch (RemoteException e) { 509 throw e.rethrowFromSystemServer(); 510 } 511 } 512 createStatusCallback( Executor executor, Consumer<Integer> statusConsumer)513 private static RemoteCallback createStatusCallback( 514 Executor executor, Consumer<Integer> statusConsumer) { 515 return new RemoteCallback( 516 result -> { 517 int status = result.getInt(STATUS_RESPONSE_BUNDLE_KEY); 518 final long identity = Binder.clearCallingIdentity(); 519 try { 520 executor.execute(() -> statusConsumer.accept(status)); 521 } finally { 522 Binder.restoreCallingIdentity(identity); 523 } 524 }); 525 } 526 createWearableSensingCallback(Executor executor)527 private IWearableSensingCallback createWearableSensingCallback(Executor executor) { 528 return new IWearableSensingCallback.Stub() { 529 530 @Override 531 public void openFile(String filename, AndroidFuture<ParcelFileDescriptor> future) { 532 Slog.d(TAG, "IWearableSensingCallback#openFile " + filename); 533 Binder.withCleanCallingIdentity( 534 () -> 535 executor.execute( 536 () -> { 537 File file = new File(mContext.getFilesDir(), filename); 538 ParcelFileDescriptor pfd = null; 539 try { 540 pfd = 541 ParcelFileDescriptor.open( 542 file, 543 ParcelFileDescriptor 544 .MODE_READ_ONLY); 545 Slog.d( 546 TAG, 547 "Successfully opened a file with" 548 + " ParcelFileDescriptor."); 549 } catch (FileNotFoundException e) { 550 Slog.e(TAG, "Cannot open file.", e); 551 } finally { 552 future.complete(pfd); 553 if (pfd != null) { 554 try { 555 pfd.close(); 556 } catch (IOException ex) { 557 Slog.e( 558 TAG, 559 "Error closing" 560 + " ParcelFileDescriptor.", 561 ex); 562 } 563 } 564 } 565 })); 566 } 567 }; 568 } 569 } 570