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