• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.callredirection;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.ServiceConnection;
23 import android.net.Uri;
24 import android.os.Binder;
25 import android.os.Handler;
26 import android.os.IBinder;
27 import android.os.Looper;
28 import android.os.RemoteException;
29 import android.os.UserHandle;
30 import android.telecom.CallRedirectionService;
31 import android.telecom.GatewayInfo;
32 import android.telecom.Log;
33 import android.telecom.Logging.Runnable;
34 import android.telecom.PhoneAccountHandle;
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.internal.telecom.ICallRedirectionAdapter;
37 import com.android.internal.telecom.ICallRedirectionService;
38 import com.android.server.telecom.Call;
39 import com.android.server.telecom.CallsManager;
40 import com.android.server.telecom.LogUtils;
41 import com.android.server.telecom.PhoneAccountRegistrar;
42 import com.android.server.telecom.TelecomSystem;
43 import com.android.server.telecom.Timeouts;
44 
45 /**
46  * A single instance of call redirection processor that handles the call redirection with
47  * user-defined {@link CallRedirectionService} and carrier {@link CallRedirectionService} for a
48  * single call.
49  *
50  * A user-defined call redirection will be performed firstly and a carrier call redirection will be
51  * performed after that; there will be a total of two call redirection cycles.
52  *
53  * A call redirection cycle is a cycle:
54  * 1) Telecom requests a call redirection of a call with a specific {@link CallRedirectionService},
55  * 2) Telecom receives the response either from a specific {@link CallRedirectionService} or from
56  * the timeout.
57  *
58  * Telecom should return to {@link CallsManager} at the end of current call redirection
59  * cycle, if
60  * 1) {@link CallRedirectionService} sends {@link CallRedirectionService#cancelCall()} response
61  * before timeout;
62  * or 2) Telecom finishes call redirection with carrier {@link CallRedirectionService}.
63  */
64 public class CallRedirectionProcessor implements CallRedirectionCallback {
65 
66     private class CallRedirectionAttempt {
67         private final ComponentName mComponentName;
68         private final String mServiceType;
69         private ServiceConnection mConnection;
70         private ICallRedirectionService mService;
71 
CallRedirectionAttempt(ComponentName componentName, String serviceType)72         private CallRedirectionAttempt(ComponentName componentName, String serviceType) {
73             mComponentName = componentName;
74             mServiceType = serviceType;
75         }
76 
process(UserHandle userHandleForCallRedirection)77         private void process(UserHandle userHandleForCallRedirection) {
78             Intent intent = new Intent(CallRedirectionService.SERVICE_INTERFACE)
79                     .setComponent(mComponentName);
80             ServiceConnection connection = new CallRedirectionServiceConnection();
81             if (mContext.bindServiceAsUser(
82                     intent,
83                     connection,
84                     Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
85                     | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
86                     userHandleForCallRedirection)) {
87                 Log.d(this, "bindService, found " + mServiceType + " call redirection service,"
88                         + " waiting for it to connect");
89                 mConnection = connection;
90             }
91         }
92 
onServiceBound(ICallRedirectionService service)93         private void onServiceBound(ICallRedirectionService service) {
94             mService = service;
95             try {
96                 // Telecom does not perform user interactions for carrier call redirection.
97                 mService.placeCall(new CallRedirectionAdapter(), mProcessedDestinationUri,
98                         mPhoneAccountHandle, mAllowInteractiveResponse
99                                 && mServiceType.equals(SERVICE_TYPE_USER_DEFINED));
100                 Log.addEvent(mCall, mServiceType.equals(SERVICE_TYPE_USER_DEFINED)
101                         ? LogUtils.Events.REDIRECTION_SENT_USER
102                         : LogUtils.Events.REDIRECTION_SENT_CARRIER, mComponentName);
103                 Log.d(this, "Requested placeCall with [Destination Uri] "
104                         + Log.pii(mProcessedDestinationUri)
105                         + " [phoneAccountHandle]" + mPhoneAccountHandle);
106             } catch (RemoteException e) {
107                 Log.e(this, e, "Failed to request with the found " + mServiceType + " call"
108                         + " redirection service");
109                 finishCallRedirection();
110             }
111         }
112 
finishCallRedirection()113         private void finishCallRedirection() {
114             if (((mServiceType.equals(SERVICE_TYPE_CARRIER)) && mIsCarrierRedirectionPending)
115                 || ((mServiceType.equals(SERVICE_TYPE_USER_DEFINED))
116                     && mIsUserDefinedRedirectionPending)) {
117                 if (mConnection != null) {
118                     // We still need to call unbind even if the service disconnected.
119                     mContext.unbindService(mConnection);
120                     mConnection = null;
121                 }
122                 mService = null;
123                 onCallRedirectionComplete(mCall);
124             }
125         }
126 
notifyTimeout()127         public void notifyTimeout() {
128             if (mService != null) {
129                 try {
130                     mService.notifyTimeout();
131                 } catch (RemoteException e) {
132                     Log.e(this, e, "Failed to notify call redirection timed out to "
133                             + mServiceType + " call redirection service");
134                 }
135             }
136             Log.i(this, "notifyTimeout: call redirection has timed out so "
137                     + "unbinding the connection");
138             if (mConnection != null) {
139                 // We still need to call unbind even if the service disconnected.
140                 mContext.unbindService(mConnection);
141                 mConnection = null;
142             }
143             mService = null;
144         }
145 
146         private class CallRedirectionServiceConnection implements ServiceConnection {
147             @Override
onServiceConnected(ComponentName componentName, IBinder service)148             public void onServiceConnected(ComponentName componentName, IBinder service) {
149                 Log.startSession("CRSC.oSC");
150                 try {
151                     synchronized (mTelecomLock) {
152                         Log.addEvent(mCall, mServiceType.equals(SERVICE_TYPE_USER_DEFINED)
153                                 ? LogUtils.Events.REDIRECTION_BOUND_USER
154                                 : LogUtils.Events.REDIRECTION_BOUND_CARRIER, componentName);
155                         onServiceBound(ICallRedirectionService.Stub.asInterface(service));
156                     }
157                 } finally {
158                     Log.endSession();
159                 }
160             }
161 
162             @Override
onServiceDisconnected(ComponentName componentName)163             public void onServiceDisconnected(ComponentName componentName) {
164                 Log.startSession("CRSC.oSD");
165                 try {
166                     synchronized (mTelecomLock) {
167                         finishCallRedirection();
168                     }
169                 } finally {
170                     Log.endSession();
171                 }
172             }
173 
174             @Override
onNullBinding(ComponentName componentName)175             public void onNullBinding(ComponentName componentName) {
176                 // Make sure we unbind the service if onBind returns null
177                 Log.startSession("CRSC.oNB");
178                 try {
179                     synchronized (mTelecomLock) {
180                         finishCallRedirection();
181                     }
182                 } finally {
183                     Log.endSession();
184                 }
185             }
186 
187             @Override
onBindingDied(ComponentName componentName)188             public void onBindingDied(ComponentName componentName) {
189                 // Make sure we unbind the service if binding died to avoid background stating
190                 // activity leaks
191                 Log.startSession("CRSC.oBD");
192                 try {
193                     synchronized (mTelecomLock) {
194                         finishCallRedirection();
195                     }
196                 } finally {
197                     Log.endSession();
198                 }
199             }
200         }
201 
202         private class CallRedirectionAdapter extends ICallRedirectionAdapter.Stub {
203             @Override
cancelCall()204             public void cancelCall() {
205                 Log.startSession("CRA.cC");
206                 long token = Binder.clearCallingIdentity();
207                 try {
208                     synchronized (mTelecomLock) {
209                         Log.d(this, "Received cancelCall from " +  mServiceType + " call"
210                                 + " redirection service");
211                         mShouldCancelCall = true;
212                         finishCallRedirection();
213                     }
214                 } finally {
215                     Binder.restoreCallingIdentity(token);
216                     Log.endSession();
217                 }
218             }
219 
220             @Override
placeCallUnmodified()221             public void placeCallUnmodified() {
222                 Log.startSession("CRA.pCU");
223                 long token = Binder.clearCallingIdentity();
224                 try {
225                     synchronized (mTelecomLock) {
226                         Log.d(this, "Received placeCallUnmodified from " +  mServiceType + " call"
227                                 + " redirection service");
228                         finishCallRedirection();
229                     }
230                 } finally {
231                     Binder.restoreCallingIdentity(token);
232                     Log.endSession();
233                 }
234             }
235 
236             @Override
redirectCall(Uri gatewayUri, PhoneAccountHandle targetPhoneAccount, boolean confirmFirst)237             public void redirectCall(Uri gatewayUri, PhoneAccountHandle targetPhoneAccount,
238                                      boolean confirmFirst) {
239                 Log.startSession("CRA.rC");
240                 long token = Binder.clearCallingIdentity();
241                 try {
242                     synchronized (mTelecomLock) {
243                         mRedirectionGatewayInfo = mCallRedirectionProcessorHelper
244                                 .getGatewayInfoFromGatewayUri(mComponentName.getPackageName(),
245                                         gatewayUri, mDestinationUri, mPostDialDigits);
246                         mPhoneAccountHandle = targetPhoneAccount;
247                         // If carrier redirects call, we should skip to notify users about
248                         // the user-defined call redirection service.
249                         mUiAction = (confirmFirst && mServiceType.equals(SERVICE_TYPE_USER_DEFINED)
250                                 && mAllowInteractiveResponse)
251                                 ? UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM : UI_TYPE_NO_ACTION;
252                         Log.d(this, "Received redirectCall with [gatewayUri]"
253                                 + Log.pii(gatewayUri) + " [phoneAccountHandle]"
254                                 + mPhoneAccountHandle + "[confirmFirst]" + confirmFirst + " from "
255                                 + mServiceType + " call redirection service");
256                         finishCallRedirection();
257                     }
258                 } finally {
259                     Binder.restoreCallingIdentity(token);
260                     Log.endSession();
261                 }
262             }
263         }
264     }
265 
266     private final Context mContext;
267     private final CallsManager mCallsManager;
268     private final Call mCall;
269     private final boolean mAllowInteractiveResponse;
270     private GatewayInfo mRedirectionGatewayInfo;
271     private final boolean mSpeakerphoneOn;
272     private final int mVideoState;
273     private final Timeouts.Adapter mTimeoutsAdapter;
274     private final TelecomSystem.SyncRoot mTelecomLock;
275     private final Handler mHandler = new Handler(Looper.getMainLooper());
276 
277     private CallRedirectionAttempt mAttempt;
278     private CallRedirectionProcessorHelper mCallRedirectionProcessorHelper;
279 
280     public static final String SERVICE_TYPE_CARRIER = "carrier";
281     public static final String SERVICE_TYPE_USER_DEFINED = "user_defined";
282     public static final String UI_TYPE_NO_ACTION = "no_action";
283     public static final String UI_TYPE_USER_DEFINED_TIMEOUT = "user_defined_timeout";
284     public static final String UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM
285             = "user_defined_ask_for_confirm";
286 
287     private PhoneAccountHandle mPhoneAccountHandle;
288     private Uri mDestinationUri;
289     /**
290      * Try to send the implemented service with processed destination uri by formatting it to E.164
291      * and removing post dial digits.
292      */
293     private Uri mProcessedDestinationUri;
294 
295     /**
296      * The post dial digits which were removed from {@link #mDestinationUri} when determining
297      * {@link #mProcessedDestinationUri}.
298      */
299     private String mPostDialDigits;
300 
301     /**
302      * Indicates if Telecom should cancel the call when the whole call redirection finishes.
303      */
304     private boolean mShouldCancelCall = false;
305     /**
306      * Indicates Telecom should handle different types of UI if need.
307      */
308     private String mUiAction = UI_TYPE_NO_ACTION;
309     /**
310      * Indicates if Telecom is waiting for a callback from a user-defined
311      * {@link CallRedirectionService}.
312      */
313     private boolean mIsUserDefinedRedirectionPending = false;
314     /**
315      * Indicates if Telecom is waiting for a callback from a carrier
316      * {@link CallRedirectionService}.
317      */
318     private boolean mIsCarrierRedirectionPending = false;
319 
CallRedirectionProcessor( Context context, CallsManager callsManager, Call call, Uri handle, PhoneAccountRegistrar phoneAccountRegistrar, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState)320     public CallRedirectionProcessor(
321             Context context,
322             CallsManager callsManager,
323             Call call,
324             Uri handle,
325             PhoneAccountRegistrar phoneAccountRegistrar,
326             GatewayInfo gatewayInfo,
327             boolean speakerphoneOn,
328             int videoState) {
329         mContext = context;
330         mCallsManager = callsManager;
331         mCall = call;
332         mDestinationUri = handle;
333         mPhoneAccountHandle = call.getTargetPhoneAccount();
334         mRedirectionGatewayInfo = gatewayInfo;
335         mSpeakerphoneOn = speakerphoneOn;
336         mVideoState = videoState;
337         mTimeoutsAdapter = callsManager.getTimeoutsAdapter();
338         mTelecomLock = callsManager.getLock();
339         /**
340          * The current rule to decide whether the implemented {@link CallRedirectionService} should
341          * allow interactive responses with users is only based on whether it is in car mode.
342          */
343         mAllowInteractiveResponse = !callsManager.getSystemStateHelper().isCarModeOrProjectionActive();
344         mCallRedirectionProcessorHelper = new CallRedirectionProcessorHelper(
345                 context, callsManager, phoneAccountRegistrar);
346         mProcessedDestinationUri = mCallRedirectionProcessorHelper.formatNumberForRedirection(
347                 mDestinationUri);
348         mPostDialDigits = mCallRedirectionProcessorHelper.getPostDialDigits(mDestinationUri);
349     }
350 
351     @Override
onCallRedirectionComplete(Call call)352     public void onCallRedirectionComplete(Call call) {
353         // synchronized on mTelecomLock to enter into Telecom.
354         mHandler.post(new Runnable("CRP.oCRC", mTelecomLock) {
355             @Override
356             public void loggedRun() {
357                 if (mIsUserDefinedRedirectionPending) {
358                     Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_USER);
359                     mIsUserDefinedRedirectionPending = false;
360                     if (mShouldCancelCall) {
361                         mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri,
362                                 mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn,
363                                 mVideoState, mShouldCancelCall, mUiAction);
364                     } else {
365                         // Use the current user for carrier call redirection
366                         performCarrierCallRedirection(UserHandle.CURRENT);
367                     }
368                 } else if (mIsCarrierRedirectionPending) {
369                     Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_CARRIER);
370                     mIsCarrierRedirectionPending = false;
371                     mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri,
372                             mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn,
373                             mVideoState, mShouldCancelCall, mUiAction);
374                 }
375             }
376         }.prepare());
377     }
378 
379     /**
380      * The entry to perform call redirection of the call from (@link CallsManager)
381      */
performCallRedirection(UserHandle userHandleForCallRedirection)382     public void performCallRedirection(UserHandle userHandleForCallRedirection) {
383         // If the Gateway Info is set with intent, only request with carrier call redirection.
384         if (mRedirectionGatewayInfo != null) {
385             // Use the current user for carrier call redirection
386             performCarrierCallRedirection(UserHandle.CURRENT);
387         } else {
388             performUserDefinedCallRedirection(userHandleForCallRedirection);
389         }
390     }
391 
performUserDefinedCallRedirection(UserHandle userHandleForCallRedirection)392     private void performUserDefinedCallRedirection(UserHandle userHandleForCallRedirection) {
393         Log.d(this, "performUserDefinedCallRedirection");
394         ComponentName componentName =
395                 mCallRedirectionProcessorHelper.
396                         getUserDefinedCallRedirectionService(userHandleForCallRedirection);
397         if (componentName != null) {
398             mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_USER_DEFINED);
399             mAttempt.process(userHandleForCallRedirection);
400             mIsUserDefinedRedirectionPending = true;
401             processTimeoutForCallRedirection(SERVICE_TYPE_USER_DEFINED);
402         } else {
403             Log.i(this, "There are no user-defined call redirection services installed on this"
404                     + " device.");
405             performCarrierCallRedirection(UserHandle.CURRENT);
406         }
407     }
408 
performCarrierCallRedirection(UserHandle userHandleForCallRedirection)409     private void performCarrierCallRedirection(UserHandle userHandleForCallRedirection) {
410         Log.d(this, "performCarrierCallRedirection");
411         ComponentName componentName =
412                 mCallRedirectionProcessorHelper.getCarrierCallRedirectionService(
413                         mPhoneAccountHandle);
414         if (componentName != null) {
415             mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_CARRIER);
416             mAttempt.process(userHandleForCallRedirection);
417             mIsCarrierRedirectionPending = true;
418             processTimeoutForCallRedirection(SERVICE_TYPE_CARRIER);
419         } else {
420             Log.i(this, "There are no carrier call redirection services installed on this"
421                     + " device.");
422             mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri,
423                     mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn, mVideoState,
424                     mShouldCancelCall, mUiAction);
425         }
426     }
427 
processTimeoutForCallRedirection(String serviceType)428     private void processTimeoutForCallRedirection(String serviceType) {
429         long timeout = serviceType.equals(SERVICE_TYPE_USER_DEFINED) ?
430             mTimeoutsAdapter.getUserDefinedCallRedirectionTimeoutMillis(
431                 mContext.getContentResolver()) : mTimeoutsAdapter
432             .getCarrierCallRedirectionTimeoutMillis(mContext.getContentResolver());
433 
434         mHandler.postDelayed(new Runnable("CRP.pTFCR", null) {
435             @Override
436             public void loggedRun() {
437                 boolean isCurrentRedirectionPending =
438                         serviceType.equals(SERVICE_TYPE_USER_DEFINED) ?
439                                 mIsUserDefinedRedirectionPending : mIsCarrierRedirectionPending;
440                 if (isCurrentRedirectionPending) {
441                     Log.i(this, serviceType + " call redirection has timed out.");
442                     Log.addEvent(mCall, serviceType.equals(SERVICE_TYPE_USER_DEFINED)
443                             ? LogUtils.Events.REDIRECTION_TIMED_OUT_USER
444                             : LogUtils.Events.REDIRECTION_TIMED_OUT_CARRIER);
445                     mAttempt.notifyTimeout();
446                     if (serviceType.equals(SERVICE_TYPE_USER_DEFINED)) {
447                         mUiAction = UI_TYPE_USER_DEFINED_TIMEOUT;
448                         mShouldCancelCall = true;
449                     }
450                     onCallRedirectionComplete(mCall);
451                 }
452             }
453         }.prepare(), timeout);
454     }
455 
456     /**
457      * Checks if Telecom can make call redirection with any available call redirection service
458      * as the specified user.
459      *
460      * @return {@code true} if it can; {@code false} otherwise.
461      */
canMakeCallRedirectionWithServiceAsUser( UserHandle userHandleForCallRedirection)462     public boolean canMakeCallRedirectionWithServiceAsUser(
463             UserHandle userHandleForCallRedirection) {
464         boolean canMakeCallRedirectionWithServiceAsUser =
465                 mCallRedirectionProcessorHelper
466                         .getUserDefinedCallRedirectionService(userHandleForCallRedirection) != null
467                         || mCallRedirectionProcessorHelper.getCarrierCallRedirectionService(
468                                 mPhoneAccountHandle) != null;
469         Log.i(this, "Can make call redirection with any "
470                 + "available service as user (" + userHandleForCallRedirection
471                 + ") : " + canMakeCallRedirectionWithServiceAsUser);
472         return canMakeCallRedirectionWithServiceAsUser;
473     }
474 
475     /**
476      * Returns the handler, for testing purposes.
477      */
478     @VisibleForTesting
getHandler()479     public Handler getHandler() {
480         return mHandler;
481     }
482 
483     /**
484      * Set CallRedirectionProcessorHelper for testing purposes.
485      */
486     @VisibleForTesting
setCallRedirectionServiceHelper( CallRedirectionProcessorHelper callRedirectionProcessorHelper)487     public void setCallRedirectionServiceHelper(
488             CallRedirectionProcessorHelper callRedirectionProcessorHelper) {
489         mCallRedirectionProcessorHelper = callRedirectionProcessorHelper;
490     }
491 }
492