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.telecom; 18 19 import static android.telecom.CallStreamingService.STREAMING_FAILED_SENDER_BINDING_ERROR; 20 21 import android.Manifest; 22 import android.annotation.SuppressLint; 23 import android.app.role.RoleManager; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.ServiceConnection; 28 import android.content.pm.PackageManager; 29 import android.content.pm.ResolveInfo; 30 import android.content.pm.ServiceInfo; 31 import android.os.Bundle; 32 import android.os.IBinder; 33 import android.os.OutcomeReceiver; 34 import android.os.RemoteException; 35 import android.os.UserHandle; 36 import android.telecom.CallException; 37 import android.telecom.CallStreamingService; 38 import android.telecom.StreamingCall; 39 import android.telecom.Log; 40 41 import com.android.internal.telecom.ICallStreamingService; 42 import com.android.server.telecom.voip.ParallelTransaction; 43 import com.android.server.telecom.voip.SerialTransaction; 44 import com.android.server.telecom.voip.VoipCallTransaction; 45 import com.android.server.telecom.voip.VoipCallTransactionResult; 46 47 import java.util.ArrayList; 48 import java.util.List; 49 import java.util.concurrent.CompletableFuture; 50 import java.util.concurrent.CompletionStage; 51 52 public class CallStreamingController extends CallsManagerListenerBase { 53 private Call mStreamingCall; 54 private TransactionalServiceWrapper mTransactionalServiceWrapper; 55 private ICallStreamingService mService; 56 private final Context mContext; 57 private CallStreamingServiceConnection mConnection; 58 private boolean mIsStreaming; 59 private final Object mLock; 60 private TelecomSystem.SyncRoot mTelecomLock; 61 CallStreamingController(Context context, TelecomSystem.SyncRoot telecomLock)62 public CallStreamingController(Context context, TelecomSystem.SyncRoot telecomLock) { 63 mLock = new Object(); 64 mContext = context; 65 mTelecomLock = telecomLock; 66 } 67 onConnectedInternal(Call call, TransactionalServiceWrapper wrapper, IBinder service)68 private void onConnectedInternal(Call call, TransactionalServiceWrapper wrapper, 69 IBinder service) throws RemoteException { 70 synchronized (mLock) { 71 Log.i(this, "onConnectedInternal: callid=%s", call.getId()); 72 Bundle extras = new Bundle(); 73 extras.putString(StreamingCall.EXTRA_CALL_ID, call.getId()); 74 mStreamingCall = call; 75 mTransactionalServiceWrapper = wrapper; 76 mService = ICallStreamingService.Stub.asInterface(service); 77 mService.setStreamingCallAdapter(new StreamingCallAdapter(mTransactionalServiceWrapper, 78 mStreamingCall, 79 mStreamingCall.getTargetPhoneAccount().getComponentName().getPackageName())); 80 mService.onCallStreamingStarted(new StreamingCall( 81 mTransactionalServiceWrapper.getComponentName(), 82 mStreamingCall.getCallerDisplayName(), 83 mStreamingCall.getHandle(), extras)); 84 mIsStreaming = true; 85 } 86 } 87 resetController()88 private void resetController() { 89 synchronized (mLock) { 90 mStreamingCall = null; 91 mTransactionalServiceWrapper = null; 92 if (mConnection != null) { 93 // Notify service streaming stopped and then unbind. 94 try { 95 mService.onCallStreamingStopped(); 96 } catch (RemoteException e) { 97 // Could not notify stop streaming; we're about to just unbind so this is 98 // unfortunate but not the end of the world. 99 Log.e(this, e, "resetController: failed to notify stop streaming."); 100 } 101 mContext.unbindService(mConnection); 102 mConnection = null; 103 } 104 mService = null; 105 mIsStreaming = false; 106 } 107 } 108 isStreaming()109 public boolean isStreaming() { 110 synchronized (mLock) { 111 return mIsStreaming; 112 } 113 } 114 115 public static class QueryCallStreamingTransaction extends VoipCallTransaction { 116 private final CallsManager mCallsManager; 117 QueryCallStreamingTransaction(CallsManager callsManager)118 public QueryCallStreamingTransaction(CallsManager callsManager) { 119 super(callsManager.getLock()); 120 mCallsManager = callsManager; 121 } 122 123 @Override processTransaction(Void v)124 public CompletableFuture<VoipCallTransactionResult> processTransaction(Void v) { 125 Log.i(this, "processTransaction"); 126 CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>(); 127 128 if (mCallsManager.getCallStreamingController().isStreaming()) { 129 future.complete(new VoipCallTransactionResult( 130 VoipCallTransactionResult.RESULT_FAILED, 131 "STREAMING_FAILED_ALREADY_STREAMING")); 132 } else { 133 future.complete(new VoipCallTransactionResult( 134 VoipCallTransactionResult.RESULT_SUCCEED, null)); 135 } 136 137 return future; 138 } 139 } 140 141 public static class AudioInterceptionTransaction extends VoipCallTransaction { 142 private Call mCall; 143 private boolean mEnterInterception; 144 AudioInterceptionTransaction(Call call, boolean enterInterception, TelecomSystem.SyncRoot lock)145 public AudioInterceptionTransaction(Call call, boolean enterInterception, 146 TelecomSystem.SyncRoot lock) { 147 super(lock); 148 mCall = call; 149 mEnterInterception = enterInterception; 150 } 151 152 @Override processTransaction(Void v)153 public CompletableFuture<VoipCallTransactionResult> processTransaction(Void v) { 154 Log.i(this, "processTransaction"); 155 CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>(); 156 157 if (mEnterInterception) { 158 mCall.startStreaming(); 159 } else { 160 mCall.stopStreaming(); 161 } 162 future.complete(new VoipCallTransactionResult(VoipCallTransactionResult.RESULT_SUCCEED, 163 null)); 164 return future; 165 } 166 } 167 getCallStreamingServiceTransaction(Context context, TransactionalServiceWrapper wrapper, Call call)168 public StreamingServiceTransaction getCallStreamingServiceTransaction(Context context, 169 TransactionalServiceWrapper wrapper, Call call) { 170 return new StreamingServiceTransaction(context, wrapper, call); 171 } 172 173 public class StreamingServiceTransaction extends VoipCallTransaction { 174 public static final String MESSAGE = "STREAMING_FAILED_NO_SENDER"; 175 private final TransactionalServiceWrapper mWrapper; 176 private final Context mContext; 177 private final UserHandle mUserHandle; 178 private final Call mCall; 179 StreamingServiceTransaction(Context context, TransactionalServiceWrapper wrapper, Call call)180 public StreamingServiceTransaction(Context context, TransactionalServiceWrapper wrapper, 181 Call call) { 182 super(mTelecomLock); 183 mWrapper = wrapper; 184 mCall = call; 185 mUserHandle = mCall.getAssociatedUser(); 186 mContext = context; 187 } 188 189 @SuppressLint("LongLogTag") 190 @Override processTransaction(Void v)191 public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) { 192 Log.i(this, "processTransaction"); 193 CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>(); 194 RoleManager roleManager = mContext.getSystemService(RoleManager.class); 195 PackageManager packageManager = mContext.getPackageManager(); 196 if (roleManager == null || packageManager == null) { 197 Log.w(this, "processTransaction: Can't find system service"); 198 future.complete(new VoipCallTransactionResult( 199 VoipCallTransactionResult.RESULT_FAILED, MESSAGE)); 200 return future; 201 } 202 203 List<String> holders = roleManager.getRoleHoldersAsUser( 204 RoleManager.ROLE_SYSTEM_CALL_STREAMING, mUserHandle); 205 if (holders.isEmpty()) { 206 Log.w(this, "processTransaction: Can't find streaming app"); 207 future.complete(new VoipCallTransactionResult( 208 VoipCallTransactionResult.RESULT_FAILED, MESSAGE)); 209 return future; 210 } 211 Log.i(this, "processTransaction: servicePackage=%s", holders.get(0)); 212 Intent serviceIntent = new Intent(CallStreamingService.SERVICE_INTERFACE); 213 serviceIntent.setPackage(holders.get(0)); 214 List<ResolveInfo> infos = packageManager.queryIntentServicesAsUser(serviceIntent, 215 PackageManager.GET_META_DATA, mUserHandle); 216 if (infos.isEmpty()) { 217 Log.w(this, "processTransaction: Can't find streaming service"); 218 future.complete(new VoipCallTransactionResult( 219 VoipCallTransactionResult.RESULT_FAILED, MESSAGE)); 220 return future; 221 } 222 223 ServiceInfo serviceInfo = infos.get(0).serviceInfo; 224 225 if (serviceInfo.permission == null || !serviceInfo.permission.equals( 226 Manifest.permission.BIND_CALL_STREAMING_SERVICE)) { 227 Log.w(this, "Must require BIND_CALL_STREAMING_SERVICE: " + 228 serviceInfo.packageName); 229 future.complete(new VoipCallTransactionResult( 230 VoipCallTransactionResult.RESULT_FAILED, MESSAGE)); 231 return future; 232 } 233 Intent intent = new Intent(CallStreamingService.SERVICE_INTERFACE); 234 intent.setComponent(serviceInfo.getComponentName()); 235 236 mConnection = new CallStreamingServiceConnection(mCall, mWrapper, future); 237 if (!mContext.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE 238 | Context.BIND_FOREGROUND_SERVICE 239 | Context.BIND_SCHEDULE_LIKE_TOP_APP, mUserHandle)) { 240 Log.w(this, "Can't bind to streaming service"); 241 future.complete(new VoipCallTransactionResult( 242 VoipCallTransactionResult.RESULT_FAILED, 243 "STREAMING_FAILED_SENDER_BINDING_ERROR")); 244 } 245 return future; 246 } 247 } 248 getUnbindStreamingServiceTransaction()249 public UnbindStreamingServiceTransaction getUnbindStreamingServiceTransaction() { 250 return new UnbindStreamingServiceTransaction(); 251 } 252 253 public class UnbindStreamingServiceTransaction extends VoipCallTransaction { UnbindStreamingServiceTransaction()254 public UnbindStreamingServiceTransaction() { 255 super(mTelecomLock); 256 } 257 258 @SuppressLint("LongLogTag") 259 @Override processTransaction(Void v)260 public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) { 261 Log.i(this, "processTransaction (unbindStreaming"); 262 CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>(); 263 264 resetController(); 265 future.complete(new VoipCallTransactionResult(VoipCallTransactionResult.RESULT_SUCCEED, 266 null)); 267 return future; 268 } 269 } 270 271 public class StartStreamingTransaction extends SerialTransaction { 272 private Call mCall; 273 StartStreamingTransaction(List<VoipCallTransaction> subTransactions, Call call, TelecomSystem.SyncRoot lock)274 public StartStreamingTransaction(List<VoipCallTransaction> subTransactions, Call call, 275 TelecomSystem.SyncRoot lock) { 276 super(subTransactions, lock); 277 mCall = call; 278 } 279 280 @Override handleTransactionFailure()281 public void handleTransactionFailure() { 282 mTransactionalServiceWrapper.stopCallStreaming(mCall); 283 } 284 } 285 getStartStreamingTransaction(CallsManager callsManager, TransactionalServiceWrapper wrapper, Call call, TelecomSystem.SyncRoot lock)286 public VoipCallTransaction getStartStreamingTransaction(CallsManager callsManager, 287 TransactionalServiceWrapper wrapper, Call call, TelecomSystem.SyncRoot lock) { 288 // start streaming transaction flow: 289 // 1. make sure there's no ongoing streaming call --> bind to EXO 290 // 2. change audio mode 291 // 3. bind to EXO 292 // If bind to EXO failed, add transaction for stop the streaming 293 294 // create list for multiple transactions 295 List<VoipCallTransaction> transactions = new ArrayList<>(); 296 transactions.add(new QueryCallStreamingTransaction(callsManager)); 297 transactions.add(new AudioInterceptionTransaction(call, true, lock)); 298 transactions.add(getCallStreamingServiceTransaction( 299 callsManager.getContext(), wrapper, call)); 300 return new StartStreamingTransaction(transactions, call, lock); 301 } 302 getStopStreamingTransaction(Call call, TelecomSystem.SyncRoot lock)303 public VoipCallTransaction getStopStreamingTransaction(Call call, TelecomSystem.SyncRoot lock) { 304 // TODO: implement this 305 // Stop streaming transaction flow: 306 List<VoipCallTransaction> transactions = new ArrayList<>(); 307 308 // 1. unbind to call streaming service 309 transactions.add(getUnbindStreamingServiceTransaction()); 310 // 2. audio route operations 311 transactions.add(new CallStreamingController.AudioInterceptionTransaction(call, 312 false, lock)); 313 return new ParallelTransaction(transactions, lock); 314 } 315 316 @Override onCallRemoved(Call call)317 public void onCallRemoved(Call call) { 318 if (mStreamingCall == call) { 319 mTransactionalServiceWrapper.stopCallStreaming(call); 320 } 321 } 322 323 @Override onCallStateChanged(Call call, int oldState, int newState)324 public void onCallStateChanged(Call call, int oldState, int newState) { 325 // TODO: make sure we are only able to stream the one call and not switch focus to another 326 // and have it streamed too 327 if (mStreamingCall == call && oldState != newState) { 328 CallStreamingStateChangeTransaction transaction = null; 329 switch (newState) { 330 case CallState.ACTIVE: 331 transaction = new CallStreamingStateChangeTransaction( 332 StreamingCall.STATE_STREAMING); 333 break; 334 case CallState.ON_HOLD: 335 transaction = new CallStreamingStateChangeTransaction( 336 StreamingCall.STATE_HOLDING); 337 break; 338 case CallState.DISCONNECTING: 339 case CallState.DISCONNECTED: 340 Log.addEvent(call, LogUtils.Events.STOP_STREAMING); 341 transaction = new CallStreamingStateChangeTransaction( 342 StreamingCall.STATE_DISCONNECTED); 343 break; 344 default: 345 // ignore 346 } 347 if (transaction != null) { 348 mTransactionalServiceWrapper.getTransactionManager().addTransaction(transaction, 349 new OutcomeReceiver<>() { 350 @Override 351 public void onResult(VoipCallTransactionResult result) { 352 // ignore 353 } 354 355 @Override 356 public void onError(CallException exception) { 357 Log.e(this, exception, "Exception when set call " 358 + "streaming state to streaming app"); 359 } 360 }); 361 } 362 } 363 } 364 365 private class CallStreamingStateChangeTransaction extends VoipCallTransaction { 366 @StreamingCall.StreamingCallState int mState; 367 CallStreamingStateChangeTransaction(@treamingCall.StreamingCallState int state)368 public CallStreamingStateChangeTransaction(@StreamingCall.StreamingCallState int state) { 369 super(mTelecomLock); 370 mState = state; 371 } 372 373 @Override processTransaction(Void v)374 public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) { 375 CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>(); 376 try { 377 mService.onCallStreamingStateChanged(mState); 378 future.complete(new VoipCallTransactionResult( 379 VoipCallTransactionResult.RESULT_SUCCEED, null)); 380 } catch (RemoteException e) { 381 future.complete(new VoipCallTransactionResult( 382 VoipCallTransactionResult.RESULT_FAILED, "Exception when request " 383 + "setting state to streaming app.")); 384 } 385 return future; 386 } 387 } 388 389 private class CallStreamingServiceConnection implements 390 ServiceConnection { 391 private Call mCall; 392 private TransactionalServiceWrapper mWrapper; 393 private CompletableFuture<VoipCallTransactionResult> mFuture; 394 CallStreamingServiceConnection(Call call, TransactionalServiceWrapper wrapper, CompletableFuture<VoipCallTransactionResult> future)395 public CallStreamingServiceConnection(Call call, TransactionalServiceWrapper wrapper, 396 CompletableFuture<VoipCallTransactionResult> future) { 397 mCall = call; 398 mWrapper = wrapper; 399 mFuture = future; 400 } 401 402 @Override onServiceConnected(ComponentName name, IBinder service)403 public void onServiceConnected(ComponentName name, IBinder service) { 404 try { 405 Log.i(this, "onServiceConnected: " + name); 406 onConnectedInternal(mCall, mWrapper, service); 407 mFuture.complete(new VoipCallTransactionResult( 408 VoipCallTransactionResult.RESULT_SUCCEED, null)); 409 } catch (RemoteException e) { 410 resetController(); 411 mFuture.complete(new VoipCallTransactionResult( 412 VoipCallTransactionResult.RESULT_FAILED, 413 StreamingServiceTransaction.MESSAGE)); 414 } 415 } 416 417 @Override onServiceDisconnected(ComponentName name)418 public void onServiceDisconnected(ComponentName name) { 419 clearBinding(); 420 } 421 422 @Override onBindingDied(ComponentName name)423 public void onBindingDied(ComponentName name) { 424 clearBinding(); 425 } 426 427 @Override onNullBinding(ComponentName name)428 public void onNullBinding(ComponentName name) { 429 clearBinding(); 430 } 431 clearBinding()432 private void clearBinding() { 433 resetController(); 434 if (!mFuture.isDone()) { 435 mFuture.complete(new VoipCallTransactionResult( 436 VoipCallTransactionResult.RESULT_FAILED, 437 "STREAMING_FAILED_SENDER_BINDING_ERROR")); 438 } else { 439 mWrapper.onCallStreamingFailed(mCall, STREAMING_FAILED_SENDER_BINDING_ERROR); 440 } 441 } 442 } 443 } 444