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 CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */, 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 CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */, 200 MESSAGE)); 201 return future; 202 } 203 204 List<String> holders = roleManager.getRoleHoldersAsUser( 205 RoleManager.ROLE_SYSTEM_CALL_STREAMING, mUserHandle); 206 if (holders.isEmpty()) { 207 Log.w(this, "processTransaction: Can't find streaming app"); 208 future.complete(new VoipCallTransactionResult( 209 CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */, 210 MESSAGE)); 211 return future; 212 } 213 Log.i(this, "processTransaction: servicePackage=%s", holders.get(0)); 214 Intent serviceIntent = new Intent(CallStreamingService.SERVICE_INTERFACE); 215 serviceIntent.setPackage(holders.get(0)); 216 List<ResolveInfo> infos = packageManager.queryIntentServicesAsUser(serviceIntent, 217 PackageManager.GET_META_DATA, mUserHandle); 218 if (infos.isEmpty()) { 219 Log.w(this, "processTransaction: Can't find streaming service"); 220 future.complete(new VoipCallTransactionResult( 221 CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */, 222 MESSAGE)); 223 return future; 224 } 225 226 ServiceInfo serviceInfo = infos.get(0).serviceInfo; 227 228 if (serviceInfo.permission == null || !serviceInfo.permission.equals( 229 Manifest.permission.BIND_CALL_STREAMING_SERVICE)) { 230 Log.w(this, "Must require BIND_CALL_STREAMING_SERVICE: " + 231 serviceInfo.packageName); 232 future.complete(new VoipCallTransactionResult( 233 CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */, 234 MESSAGE)); 235 return future; 236 } 237 Intent intent = new Intent(CallStreamingService.SERVICE_INTERFACE); 238 intent.setComponent(serviceInfo.getComponentName()); 239 240 mConnection = new CallStreamingServiceConnection(mCall, mWrapper, future); 241 if (!mContext.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE 242 | Context.BIND_FOREGROUND_SERVICE 243 | Context.BIND_SCHEDULE_LIKE_TOP_APP, mUserHandle)) { 244 Log.w(this, "Can't bind to streaming service"); 245 future.complete(new VoipCallTransactionResult( 246 CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */, 247 "STREAMING_FAILED_SENDER_BINDING_ERROR")); 248 } 249 return future; 250 } 251 } 252 getUnbindStreamingServiceTransaction()253 public UnbindStreamingServiceTransaction getUnbindStreamingServiceTransaction() { 254 return new UnbindStreamingServiceTransaction(); 255 } 256 257 public class UnbindStreamingServiceTransaction extends VoipCallTransaction { UnbindStreamingServiceTransaction()258 public UnbindStreamingServiceTransaction() { 259 super(mTelecomLock); 260 } 261 262 @SuppressLint("LongLogTag") 263 @Override processTransaction(Void v)264 public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) { 265 Log.i(this, "processTransaction (unbindStreaming"); 266 CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>(); 267 268 resetController(); 269 future.complete(new VoipCallTransactionResult(VoipCallTransactionResult.RESULT_SUCCEED, 270 null)); 271 return future; 272 } 273 } 274 275 public class StartStreamingTransaction extends SerialTransaction { 276 private Call mCall; 277 StartStreamingTransaction(List<VoipCallTransaction> subTransactions, Call call, TelecomSystem.SyncRoot lock)278 public StartStreamingTransaction(List<VoipCallTransaction> subTransactions, Call call, 279 TelecomSystem.SyncRoot lock) { 280 super(subTransactions, lock); 281 mCall = call; 282 } 283 284 @Override handleTransactionFailure()285 public void handleTransactionFailure() { 286 mTransactionalServiceWrapper.stopCallStreaming(mCall); 287 } 288 } 289 getStartStreamingTransaction(CallsManager callsManager, TransactionalServiceWrapper wrapper, Call call, TelecomSystem.SyncRoot lock)290 public VoipCallTransaction getStartStreamingTransaction(CallsManager callsManager, 291 TransactionalServiceWrapper wrapper, Call call, TelecomSystem.SyncRoot lock) { 292 // start streaming transaction flow: 293 // 1. make sure there's no ongoing streaming call --> bind to EXO 294 // 2. change audio mode 295 // 3. bind to EXO 296 // If bind to EXO failed, add transaction for stop the streaming 297 298 // create list for multiple transactions 299 List<VoipCallTransaction> transactions = new ArrayList<>(); 300 transactions.add(new QueryCallStreamingTransaction(callsManager)); 301 transactions.add(new AudioInterceptionTransaction(call, true, lock)); 302 transactions.add(getCallStreamingServiceTransaction( 303 callsManager.getContext(), wrapper, call)); 304 return new StartStreamingTransaction(transactions, call, lock); 305 } 306 getStopStreamingTransaction(Call call, TelecomSystem.SyncRoot lock)307 public VoipCallTransaction getStopStreamingTransaction(Call call, TelecomSystem.SyncRoot lock) { 308 // TODO: implement this 309 // Stop streaming transaction flow: 310 List<VoipCallTransaction> transactions = new ArrayList<>(); 311 312 // 1. unbind to call streaming service 313 transactions.add(getUnbindStreamingServiceTransaction()); 314 // 2. audio route operations 315 transactions.add(new CallStreamingController.AudioInterceptionTransaction(call, 316 false, lock)); 317 return new ParallelTransaction(transactions, lock); 318 } 319 320 @Override onCallRemoved(Call call)321 public void onCallRemoved(Call call) { 322 if (mStreamingCall == call) { 323 mTransactionalServiceWrapper.stopCallStreaming(call); 324 } 325 } 326 327 @Override onCallStateChanged(Call call, int oldState, int newState)328 public void onCallStateChanged(Call call, int oldState, int newState) { 329 // TODO: make sure we are only able to stream the one call and not switch focus to another 330 // and have it streamed too 331 if (mStreamingCall == call && oldState != newState) { 332 CallStreamingStateChangeTransaction transaction = null; 333 switch (newState) { 334 case CallState.ACTIVE: 335 transaction = new CallStreamingStateChangeTransaction( 336 StreamingCall.STATE_STREAMING); 337 break; 338 case CallState.ON_HOLD: 339 transaction = new CallStreamingStateChangeTransaction( 340 StreamingCall.STATE_HOLDING); 341 break; 342 case CallState.DISCONNECTING: 343 case CallState.DISCONNECTED: 344 Log.addEvent(call, LogUtils.Events.STOP_STREAMING); 345 transaction = new CallStreamingStateChangeTransaction( 346 StreamingCall.STATE_DISCONNECTED); 347 break; 348 default: 349 // ignore 350 } 351 if (transaction != null) { 352 mTransactionalServiceWrapper.getTransactionManager().addTransaction(transaction, 353 new OutcomeReceiver<>() { 354 @Override 355 public void onResult(VoipCallTransactionResult result) { 356 // ignore 357 } 358 359 @Override 360 public void onError(CallException exception) { 361 Log.e(this, exception, "Exception when set call " 362 + "streaming state to streaming app"); 363 } 364 }); 365 } 366 } 367 } 368 369 private class CallStreamingStateChangeTransaction extends VoipCallTransaction { 370 @StreamingCall.StreamingCallState int mState; 371 CallStreamingStateChangeTransaction(@treamingCall.StreamingCallState int state)372 public CallStreamingStateChangeTransaction(@StreamingCall.StreamingCallState int state) { 373 super(mTelecomLock); 374 mState = state; 375 } 376 377 @Override processTransaction(Void v)378 public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) { 379 CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>(); 380 try { 381 mService.onCallStreamingStateChanged(mState); 382 future.complete(new VoipCallTransactionResult( 383 VoipCallTransactionResult.RESULT_SUCCEED, null)); 384 } catch (RemoteException e) { 385 future.complete(new VoipCallTransactionResult( 386 CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */, 387 "Exception when request " 388 + "setting state to streaming app.")); 389 } 390 return future; 391 } 392 } 393 394 private class CallStreamingServiceConnection implements 395 ServiceConnection { 396 private Call mCall; 397 private TransactionalServiceWrapper mWrapper; 398 private CompletableFuture<VoipCallTransactionResult> mFuture; 399 CallStreamingServiceConnection(Call call, TransactionalServiceWrapper wrapper, CompletableFuture<VoipCallTransactionResult> future)400 public CallStreamingServiceConnection(Call call, TransactionalServiceWrapper wrapper, 401 CompletableFuture<VoipCallTransactionResult> future) { 402 mCall = call; 403 mWrapper = wrapper; 404 mFuture = future; 405 } 406 407 @Override onServiceConnected(ComponentName name, IBinder service)408 public void onServiceConnected(ComponentName name, IBinder service) { 409 try { 410 Log.i(this, "onServiceConnected: " + name); 411 onConnectedInternal(mCall, mWrapper, service); 412 mFuture.complete(new VoipCallTransactionResult( 413 VoipCallTransactionResult.RESULT_SUCCEED, null)); 414 } catch (RemoteException e) { 415 resetController(); 416 mFuture.complete(new VoipCallTransactionResult( 417 CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */, 418 StreamingServiceTransaction.MESSAGE)); 419 } 420 } 421 422 @Override onServiceDisconnected(ComponentName name)423 public void onServiceDisconnected(ComponentName name) { 424 clearBinding(); 425 } 426 427 @Override onBindingDied(ComponentName name)428 public void onBindingDied(ComponentName name) { 429 clearBinding(); 430 } 431 432 @Override onNullBinding(ComponentName name)433 public void onNullBinding(ComponentName name) { 434 clearBinding(); 435 } 436 clearBinding()437 private void clearBinding() { 438 resetController(); 439 if (!mFuture.isDone()) { 440 mFuture.complete(new VoipCallTransactionResult( 441 CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */, 442 "STREAMING_FAILED_SENDER_BINDING_ERROR")); 443 } else { 444 mWrapper.onCallStreamingFailed(mCall, STREAMING_FAILED_SENDER_BINDING_ERROR); 445 } 446 } 447 } 448 } 449