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