• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.CallException.CODE_CALL_IS_NOT_BEING_TRACKED;
20 import static android.telecom.CallException.TRANSACTION_EXCEPTION_KEY;
21 import static android.telecom.TelecomManager.TELECOM_TRANSACTION_SUCCESS;
22 
23 import android.content.ComponentName;
24 import android.os.Bundle;
25 import android.os.IBinder;
26 import android.os.OutcomeReceiver;
27 import android.os.RemoteException;
28 import android.os.ResultReceiver;
29 import android.telecom.CallEndpoint;
30 import android.telecom.CallException;
31 import android.telecom.CallStreamingService;
32 import android.telecom.DisconnectCause;
33 import android.telecom.Log;
34 import android.telecom.PhoneAccountHandle;
35 import android.text.TextUtils;
36 
37 import androidx.annotation.VisibleForTesting;
38 
39 import com.android.internal.telecom.ICallControl;
40 import com.android.internal.telecom.ICallEventCallback;
41 import com.android.server.telecom.voip.CallEventCallbackAckTransaction;
42 import com.android.server.telecom.voip.EndpointChangeTransaction;
43 import com.android.server.telecom.voip.HoldCallTransaction;
44 import com.android.server.telecom.voip.EndCallTransaction;
45 import com.android.server.telecom.voip.MaybeHoldCallForNewCallTransaction;
46 import com.android.server.telecom.voip.ParallelTransaction;
47 import com.android.server.telecom.voip.RequestNewActiveCallTransaction;
48 import com.android.server.telecom.voip.SerialTransaction;
49 import com.android.server.telecom.voip.TransactionManager;
50 import com.android.server.telecom.voip.VoipCallTransaction;
51 import com.android.server.telecom.voip.VoipCallTransactionResult;
52 
53 import java.util.ArrayList;
54 import java.util.List;
55 import java.util.Locale;
56 import java.util.Set;
57 import java.util.concurrent.ConcurrentHashMap;
58 
59 /**
60  * Implements {@link android.telecom.CallEventCallback} and {@link android.telecom.CallControl}
61  * on a per-client basis which is tied to a {@link PhoneAccountHandle}
62  */
63 public class TransactionalServiceWrapper implements
64         ConnectionServiceFocusManager.ConnectionServiceFocus {
65     private static final String TAG = TransactionalServiceWrapper.class.getSimpleName();
66 
67     // CallControl : Client (ex. voip app) --> Telecom
68     public static final String SET_ACTIVE = "SetActive";
69     public static final String SET_INACTIVE = "SetInactive";
70     public static final String ANSWER = "Answer";
71     public static final String DISCONNECT = "Disconnect";
72     public static final String START_STREAMING = "StartStreaming";
73 
74     // CallEventCallback : Telecom --> Client (ex. voip app)
75     public static final String ON_SET_ACTIVE = "onSetActive";
76     public static final String ON_SET_INACTIVE = "onSetInactive";
77     public static final String ON_ANSWER = "onAnswer";
78     public static final String ON_DISCONNECT = "onDisconnect";
79     public static final String ON_STREAMING_STARTED = "onStreamingStarted";
80 
81     private final CallsManager mCallsManager;
82     private final ICallEventCallback mICallEventCallback;
83     private final PhoneAccountHandle mPhoneAccountHandle;
84     private final TransactionalServiceRepository mRepository;
85     private ConnectionServiceFocusManager.ConnectionServiceFocusListener mConnSvrFocusListener;
86     // init when constructor is called
87     private final ConcurrentHashMap<String, Call> mTrackedCalls = new ConcurrentHashMap<>();
88     private final TelecomSystem.SyncRoot mLock;
89     private final String mPackageName;
90     // needs to be non-final for testing
91     private TransactionManager mTransactionManager;
92     private CallStreamingController mStreamingController;
93 
94 
95     // Each TransactionalServiceWrapper should have their own Binder.DeathRecipient to clean up
96     // any calls in the event the application crashes or is force stopped.
97     private final IBinder.DeathRecipient mAppDeathListener = new IBinder.DeathRecipient() {
98         @Override
99         public void binderDied() {
100             Log.i(TAG, "binderDied: for package=[%s]; cleaning calls", mPackageName);
101             cleanupTransactionalServiceWrapper();
102             mICallEventCallback.asBinder().unlinkToDeath(this, 0);
103         }
104     };
105 
TransactionalServiceWrapper(ICallEventCallback callEventCallback, CallsManager callsManager, PhoneAccountHandle phoneAccountHandle, Call call, TransactionalServiceRepository repo)106     public TransactionalServiceWrapper(ICallEventCallback callEventCallback,
107             CallsManager callsManager, PhoneAccountHandle phoneAccountHandle, Call call,
108             TransactionalServiceRepository repo) {
109         // passed args
110         mICallEventCallback = callEventCallback;
111         mCallsManager = callsManager;
112         mPhoneAccountHandle = phoneAccountHandle;
113         mTrackedCalls.put(call.getId(), call); // service is now tracking its first call
114         mRepository = repo;
115         // init instance vars
116         mPackageName = phoneAccountHandle.getComponentName().getPackageName();
117         mTransactionManager = TransactionManager.getInstance();
118         mStreamingController = mCallsManager.getCallStreamingController();
119         mLock = mCallsManager.getLock();
120         setDeathRecipient(callEventCallback);
121     }
122 
123     @VisibleForTesting
setTransactionManager(TransactionManager transactionManager)124     public void setTransactionManager(TransactionManager transactionManager) {
125         mTransactionManager = transactionManager;
126     }
127 
getTransactionManager()128     public TransactionManager getTransactionManager() {
129         return mTransactionManager;
130     }
131 
getPhoneAccountHandle()132     public PhoneAccountHandle getPhoneAccountHandle() {
133         return mPhoneAccountHandle;
134     }
135 
trackCall(Call call)136     public void trackCall(Call call) {
137         synchronized (mLock) {
138             if (call != null) {
139                 mTrackedCalls.put(call.getId(), call);
140             }
141         }
142     }
143 
144     @VisibleForTesting
untrackCall(Call call)145     public boolean untrackCall(Call call) {
146         Call removedCall = null;
147         synchronized (mLock) {
148             if (call != null) {
149                 removedCall = mTrackedCalls.remove(call.getId());
150                 if (mTrackedCalls.size() == 0) {
151                     mRepository.removeServiceWrapper(mPhoneAccountHandle);
152                 }
153             }
154         }
155         Log.i(TAG, "removedCall call=" + removedCall);
156         return removedCall != null;
157     }
158 
159     @VisibleForTesting
getNumberOfTrackedCalls()160     public int getNumberOfTrackedCalls() {
161         int callCount = 0;
162         synchronized (mLock) {
163             callCount = mTrackedCalls.size();
164         }
165         return callCount;
166     }
167 
cleanupTransactionalServiceWrapper()168     public void cleanupTransactionalServiceWrapper() {
169         for (Call call : mTrackedCalls.values()) {
170             mCallsManager.markCallAsDisconnected(call,
171                     new DisconnectCause(DisconnectCause.ERROR, "process died"));
172             mCallsManager.removeCall(call); // This will clear mTrackedCalls && ClientTWS
173         }
174     }
175 
176     /***
177      *********************************************************************************************
178      **                        ICallControl: Client --> Server                                **
179      **********************************************************************************************
180      */
181     public final ICallControl mICallControl = new ICallControl.Stub() {
182         @Override
183         public void setActive(String callId, android.os.ResultReceiver callback)
184                 throws RemoteException {
185             try {
186                 Log.startSession("TSW.sA");
187                 createTransactions(callId, callback, SET_ACTIVE);
188             } finally {
189                 Log.endSession();
190             }
191         }
192 
193         @Override
194         public void answer(int videoState, String callId, android.os.ResultReceiver callback)
195                 throws RemoteException {
196             try {
197                 Log.startSession("TSW.a");
198                 createTransactions(callId, callback, ANSWER, videoState);
199             } finally {
200                 Log.endSession();
201             }
202         }
203 
204         @Override
205         public void setInactive(String callId, android.os.ResultReceiver callback)
206                 throws RemoteException {
207             try {
208                 Log.startSession("TSW.sI");
209                 createTransactions(callId, callback, SET_INACTIVE);
210             } finally {
211                 Log.endSession();
212             }
213         }
214 
215         @Override
216         public void disconnect(String callId, DisconnectCause disconnectCause,
217                 android.os.ResultReceiver callback)
218                 throws RemoteException {
219             try {
220                 Log.startSession("TSW.d");
221                 createTransactions(callId, callback, DISCONNECT, disconnectCause);
222             } finally {
223                 Log.endSession();
224             }
225         }
226 
227         @Override
228         public void startCallStreaming(String callId, android.os.ResultReceiver callback)
229                 throws RemoteException {
230             try {
231                 Log.startSession("TSW.sCS");
232                 createTransactions(callId, callback, START_STREAMING);
233             } finally {
234                 Log.endSession();
235             }
236         }
237 
238         private void createTransactions(String callId, ResultReceiver callback, String action,
239                 Object... objects) {
240             Log.d(TAG, "createTransactions: callId=" + callId);
241             Call call = mTrackedCalls.get(callId);
242             if (call != null) {
243                 switch (action) {
244                     case SET_ACTIVE:
245                         handleCallControlNewCallFocusTransactions(call, SET_ACTIVE,
246                                 false /* isAnswer */, 0/*VideoState (ignored)*/, callback);
247                         break;
248                     case ANSWER:
249                         handleCallControlNewCallFocusTransactions(call, ANSWER,
250                                 true /* isAnswer */, (int) objects[0] /*VideoState*/, callback);
251                         break;
252                     case DISCONNECT:
253                         addTransactionsToManager(new EndCallTransaction(mCallsManager,
254                                 (DisconnectCause) objects[0], call), callback);
255                         break;
256                     case SET_INACTIVE:
257                         addTransactionsToManager(
258                                 new HoldCallTransaction(mCallsManager, call), callback);
259                         break;
260                     case START_STREAMING:
261                         addTransactionsToManager(mStreamingController.getStartStreamingTransaction(mCallsManager,
262                                 TransactionalServiceWrapper.this, call, mLock), callback);
263                         break;
264                 }
265             } else {
266                 Bundle exceptionBundle = new Bundle();
267                 exceptionBundle.putParcelable(TRANSACTION_EXCEPTION_KEY,
268                         new CallException(TextUtils.formatSimple(
269                         "Telecom cannot process [%s] because the call with id=[%s] is no longer "
270                                 + "being tracked. This is most likely a result of the call "
271                                 + "already being disconnected and removed. Try re-adding the call"
272                                 + " via TelecomManager#addCall", action, callId),
273                                 CODE_CALL_IS_NOT_BEING_TRACKED));
274                 callback.send(CODE_CALL_IS_NOT_BEING_TRACKED, exceptionBundle);
275             }
276         }
277 
278         // The client is request their VoIP call state go ACTIVE/ANSWERED.
279         // This request is originating from the VoIP application.
280         private void handleCallControlNewCallFocusTransactions(Call call, String action,
281                 boolean isAnswer, int potentiallyNewVideoState, ResultReceiver callback) {
282             mTransactionManager.addTransaction(createSetActiveTransactions(call),
283                     new OutcomeReceiver<>() {
284                         @Override
285                         public void onResult(VoipCallTransactionResult result) {
286                             Log.i(TAG, String.format(Locale.US,
287                                     "%s: onResult: callId=[%s]", action, call.getId()));
288                             if (isAnswer) {
289                                 call.setVideoState(potentiallyNewVideoState);
290                             }
291                             callback.send(TELECOM_TRANSACTION_SUCCESS, new Bundle());
292                         }
293 
294                         @Override
295                         public void onError(CallException exception) {
296                             Bundle extras = new Bundle();
297                             extras.putParcelable(TRANSACTION_EXCEPTION_KEY, exception);
298                             callback.send(exception == null ? CallException.CODE_ERROR_UNKNOWN :
299                                     exception.getCode(), extras);
300                         }
301                     });
302         }
303 
304         @Override
305         public void requestCallEndpointChange(CallEndpoint endpoint, ResultReceiver callback) {
306             try {
307                 Log.startSession("TSW.rCEC");
308                 addTransactionsToManager(new EndpointChangeTransaction(endpoint, mCallsManager),
309                         callback);
310             } finally {
311                 Log.endSession();
312             }
313         }
314 
315         /**
316          * Application would like to inform InCallServices of an event
317          */
318         @Override
319         public void sendEvent(String callId, String event, Bundle extras) {
320             try {
321                 Log.startSession("TSW.sE");
322                 Call call = mTrackedCalls.get(callId);
323                 if (call != null) {
324                     call.onConnectionEvent(event, extras);
325                 } else {
326                     Log.i(TAG,
327                             "sendEvent: was called but there is no call with id=[%s] cannot be "
328                                     + "found. Most likely the call has been disconnected");
329                 }
330             } finally {
331                 Log.endSession();
332             }
333         }
334     };
335 
addTransactionsToManager(VoipCallTransaction transaction, ResultReceiver callback)336     public void addTransactionsToManager(VoipCallTransaction transaction,
337             ResultReceiver callback) {
338         Log.d(TAG, "addTransactionsToManager");
339 
340         mTransactionManager.addTransaction(transaction, new OutcomeReceiver<>() {
341             @Override
342             public void onResult(VoipCallTransactionResult result) {
343                 Log.d(TAG, "addTransactionsToManager: onResult:");
344                 callback.send(TELECOM_TRANSACTION_SUCCESS, new Bundle());
345             }
346 
347             @Override
348             public void onError(CallException exception) {
349                 Log.d(TAG, "addTransactionsToManager: onError");
350                 Bundle extras = new Bundle();
351                 extras.putParcelable(TRANSACTION_EXCEPTION_KEY, exception);
352                 callback.send(exception == null ? CallException.CODE_ERROR_UNKNOWN :
353                         exception.getCode(), extras);
354             }
355         });
356     }
357 
getICallControl()358     public ICallControl getICallControl() {
359         return mICallControl;
360     }
361 
362     /***
363      *********************************************************************************************
364      **                    ICallEventCallback: Server --> Client                                **
365      **********************************************************************************************
366      */
367 
onSetActive(Call call)368     public void onSetActive(Call call) {
369         try {
370             Log.startSession("TSW.oSA");
371             Log.d(TAG, String.format(Locale.US, "onSetActive: callId=[%s]", call.getId()));
372             handleCallEventCallbackNewFocus(call, ON_SET_ACTIVE, false /*isAnswerRequest*/,
373                     0 /*VideoState*/);
374         } finally {
375             Log.endSession();
376         }
377     }
378 
onAnswer(Call call, int videoState)379     public void onAnswer(Call call, int videoState) {
380         try {
381             Log.startSession("TSW.oA");
382             Log.d(TAG, String.format(Locale.US, "onAnswer: callId=[%s]", call.getId()));
383             handleCallEventCallbackNewFocus(call, ON_ANSWER, true /*isAnswerRequest*/,
384                     videoState /*VideoState*/);
385         } finally {
386             Log.endSession();
387         }
388     }
389 
390     // handle a CallEventCallback to set a call ACTIVE/ANSWERED. Must get ack from client since the
391     // request has come from another source (ex. Android Auto is requesting a call to go active)
handleCallEventCallbackNewFocus(Call call, String action, boolean isAnswerRequest, int potentiallyNewVideoState)392     private void handleCallEventCallbackNewFocus(Call call, String action, boolean isAnswerRequest,
393             int potentiallyNewVideoState) {
394         // save CallsManager state before sending client state changes
395         Call foregroundCallBeforeSwap = mCallsManager.getForegroundCall();
396         boolean wasActive = foregroundCallBeforeSwap != null && foregroundCallBeforeSwap.isActive();
397 
398         SerialTransaction serialTransactions = createSetActiveTransactions(call);
399         // 3. get ack from client (that the requested call can go active)
400         if (isAnswerRequest) {
401             serialTransactions.appendTransaction(
402                     new CallEventCallbackAckTransaction(mICallEventCallback,
403                             action, call.getId(), potentiallyNewVideoState, mLock));
404         } else {
405             serialTransactions.appendTransaction(
406                     new CallEventCallbackAckTransaction(mICallEventCallback,
407                             action, call.getId(), mLock));
408         }
409 
410         // do CallsManager workload before asking client and
411         //   reset CallsManager state if client does NOT ack
412         mTransactionManager.addTransaction(serialTransactions,
413                 new OutcomeReceiver<>() {
414                     @Override
415                     public void onResult(VoipCallTransactionResult result) {
416                         Log.i(TAG, String.format(Locale.US,
417                                 "%s: onResult: callId=[%s]", action, call.getId()));
418                         if (isAnswerRequest) {
419                             call.setVideoState(potentiallyNewVideoState);
420                         }
421                     }
422 
423                     @Override
424                     public void onError(CallException exception) {
425                         if (isAnswerRequest) {
426                             // This also sends the signal to untrack from TSW and the client_TSW
427                             removeCallFromCallsManager(call,
428                                     new DisconnectCause(DisconnectCause.REJECTED,
429                                             "client rejected to answer the call;"
430                                                     + " force disconnecting"));
431                         } else {
432                             mCallsManager.markCallAsOnHold(call);
433                         }
434                         maybeResetForegroundCall(foregroundCallBeforeSwap, wasActive);
435                     }
436                 });
437     }
438 
439 
onSetInactive(Call call)440     public void onSetInactive(Call call) {
441         try {
442             Log.startSession("TSW.oSI");
443             Log.i(TAG, String.format(Locale.US, "onSetInactive: callId=[%s]", call.getId()));
444             mTransactionManager.addTransaction(
445                     new CallEventCallbackAckTransaction(mICallEventCallback,
446                             ON_SET_INACTIVE, call.getId(), mLock), new OutcomeReceiver<>() {
447                         @Override
448                         public void onResult(VoipCallTransactionResult result) {
449                             mCallsManager.markCallAsOnHold(call);
450                         }
451 
452                         @Override
453                         public void onError(CallException exception) {
454                             Log.i(TAG, "onSetInactive: onError: with e=[%e]", exception);
455                         }
456                     });
457         } finally {
458             Log.endSession();
459         }
460     }
461 
onDisconnect(Call call, DisconnectCause cause)462     public void onDisconnect(Call call, DisconnectCause cause) {
463         try {
464             Log.startSession("TSW.oD");
465             Log.d(TAG, String.format(Locale.US, "onDisconnect: callId=[%s]", call.getId()));
466 
467             mTransactionManager.addTransaction(
468                     new CallEventCallbackAckTransaction(mICallEventCallback, ON_DISCONNECT,
469                             call.getId(), cause, mLock), new OutcomeReceiver<>() {
470                         @Override
471                         public void onResult(VoipCallTransactionResult result) {
472                             removeCallFromCallsManager(call, cause);
473                         }
474 
475                         @Override
476                         public void onError(CallException exception) {
477                             removeCallFromCallsManager(call, cause);
478                         }
479                     }
480             );
481         } finally {
482             Log.endSession();
483         }
484     }
485 
onCallStreamingStarted(Call call)486     public void onCallStreamingStarted(Call call) {
487         try {
488             Log.startSession("TSW.oCSS");
489             Log.d(TAG, String.format(Locale.US, "onCallStreamingStarted: callId=[%s]",
490                     call.getId()));
491 
492             mTransactionManager.addTransaction(
493                     new CallEventCallbackAckTransaction(mICallEventCallback, ON_STREAMING_STARTED,
494                             call.getId(), mLock), new OutcomeReceiver<>() {
495                         @Override
496                         public void onResult(VoipCallTransactionResult result) {
497                         }
498 
499                         @Override
500                         public void onError(CallException exception) {
501                             Log.i(TAG, "onCallStreamingStarted: onError: with e=[%e]",
502                                     exception);
503                             stopCallStreaming(call);
504                         }
505                     }
506             );
507         } finally {
508             Log.endSession();
509         }
510     }
511 
onCallStreamingFailed(Call call, @CallStreamingService.StreamingFailedReason int streamingFailedReason)512     public void onCallStreamingFailed(Call call,
513             @CallStreamingService.StreamingFailedReason int streamingFailedReason) {
514         if (call != null) {
515             try {
516                 mICallEventCallback.onCallStreamingFailed(call.getId(), streamingFailedReason);
517             } catch (RemoteException e) {
518             }
519         }
520     }
521 
onCallEndpointChanged(Call call, CallEndpoint endpoint)522     public void onCallEndpointChanged(Call call, CallEndpoint endpoint) {
523         if (call != null) {
524             try {
525                 mICallEventCallback.onCallEndpointChanged(call.getId(), endpoint);
526             } catch (RemoteException e) {
527             }
528         }
529     }
530 
onAvailableCallEndpointsChanged(Call call, Set<CallEndpoint> endpoints)531     public void onAvailableCallEndpointsChanged(Call call, Set<CallEndpoint> endpoints) {
532         if (call != null) {
533             try {
534                 mICallEventCallback.onAvailableCallEndpointsChanged(call.getId(),
535                         endpoints.stream().toList());
536             } catch (RemoteException e) {
537             }
538         }
539     }
540 
onMuteStateChanged(Call call, boolean isMuted)541     public void onMuteStateChanged(Call call, boolean isMuted) {
542         if (call != null) {
543             try {
544                 mICallEventCallback.onMuteStateChanged(call.getId(), isMuted);
545             } catch (RemoteException e) {
546             }
547         }
548     }
549 
removeCallFromWrappers(Call call)550     public void removeCallFromWrappers(Call call) {
551         if (call != null) {
552             try {
553                 // remove the call from frameworks wrapper (client side)
554                 mICallEventCallback.removeCallFromTransactionalServiceWrapper(call.getId());
555             } catch (RemoteException e) {
556             }
557             // remove the call from this class/wrapper (server side)
558             untrackCall(call);
559         }
560     }
561 
onEvent(Call call, String event, Bundle extras)562     public void onEvent(Call call, String event, Bundle extras) {
563         if (call != null) {
564             try {
565                 mICallEventCallback.onEvent(call.getId(), event, extras);
566             } catch (RemoteException e) {
567             }
568         }
569     }
570 
571     /***
572      *********************************************************************************************
573      **                                Helpers                                                  **
574      **********************************************************************************************
575      */
maybeResetForegroundCall(Call foregroundCallBeforeSwap, boolean wasActive)576     private void maybeResetForegroundCall(Call foregroundCallBeforeSwap, boolean wasActive) {
577         if (foregroundCallBeforeSwap == null) {
578             return;
579         }
580         if (wasActive && !foregroundCallBeforeSwap.isActive()) {
581             mCallsManager.markCallAsActive(foregroundCallBeforeSwap);
582         }
583     }
584 
removeCallFromCallsManager(Call call, DisconnectCause cause)585     private void removeCallFromCallsManager(Call call, DisconnectCause cause) {
586         if (cause.getCode() != DisconnectCause.REJECTED) {
587             mCallsManager.markCallAsDisconnected(call, cause);
588         }
589         mCallsManager.removeCall(call);
590     }
591 
createSetActiveTransactions(Call call)592     private SerialTransaction createSetActiveTransactions(Call call) {
593         // create list for multiple transactions
594         List<VoipCallTransaction> transactions = new ArrayList<>();
595 
596         // potentially hold the current active call in order to set a new call (active/answered)
597         transactions.add(new MaybeHoldCallForNewCallTransaction(mCallsManager, call));
598         // And request a new focus call update
599         transactions.add(new RequestNewActiveCallTransaction(mCallsManager, call));
600 
601         return new SerialTransaction(transactions, mLock);
602     }
603 
setDeathRecipient(ICallEventCallback callEventCallback)604     private void setDeathRecipient(ICallEventCallback callEventCallback) {
605         try {
606             callEventCallback.asBinder().linkToDeath(mAppDeathListener, 0);
607         } catch (Exception e) {
608             Log.w(TAG, "setDeathRecipient: hit exception=[%s] trying to link binder to death",
609                     e.toString());
610         }
611     }
612 
613     /***
614      *********************************************************************************************
615      **                    FocusManager                                                       **
616      **********************************************************************************************
617      */
618 
619     @Override
connectionServiceFocusLost()620     public void connectionServiceFocusLost() {
621         if (mConnSvrFocusListener != null) {
622             mConnSvrFocusListener.onConnectionServiceReleased(this);
623         }
624         Log.i(TAG, String.format(Locale.US, "connectionServiceFocusLost for package=[%s]",
625                 mPackageName));
626     }
627 
628     @Override
connectionServiceFocusGained()629     public void connectionServiceFocusGained() {
630         Log.i(TAG, String.format(Locale.US, "connectionServiceFocusGained for package=[%s]",
631                 mPackageName));
632     }
633 
634     @Override
setConnectionServiceFocusListener( ConnectionServiceFocusManager.ConnectionServiceFocusListener listener)635     public void setConnectionServiceFocusListener(
636             ConnectionServiceFocusManager.ConnectionServiceFocusListener listener) {
637         mConnSvrFocusListener = listener;
638     }
639 
640     @Override
getComponentName()641     public ComponentName getComponentName() {
642         return mPhoneAccountHandle.getComponentName();
643     }
644 
645     /***
646      *********************************************************************************************
647      **                    CallStreaming                                                        **
648      *********************************************************************************************
649      */
650 
stopCallStreaming(Call call)651     public void stopCallStreaming(Call call) {
652         Log.i(this, "stopCallStreaming; callid=%s", call.getId());
653         if (call != null && call.isStreaming()) {
654             VoipCallTransaction stopStreamingTransaction = mStreamingController
655                     .getStopStreamingTransaction(call, mLock);
656             addTransactionsToManager(stopStreamingTransaction, new ResultReceiver(null));
657         }
658     }
659 }
660