• 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()77         private void process() {
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                     UserHandle.CURRENT)) {
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         }
137 
138         private class CallRedirectionServiceConnection implements ServiceConnection {
139             @Override
onServiceConnected(ComponentName componentName, IBinder service)140             public void onServiceConnected(ComponentName componentName, IBinder service) {
141                 Log.startSession("CRSC.oSC");
142                 try {
143                     synchronized (mTelecomLock) {
144                         Log.addEvent(mCall, mServiceType.equals(SERVICE_TYPE_USER_DEFINED)
145                                 ? LogUtils.Events.REDIRECTION_BOUND_USER
146                                 : LogUtils.Events.REDIRECTION_BOUND_CARRIER, componentName);
147                         onServiceBound(ICallRedirectionService.Stub.asInterface(service));
148                     }
149                 } finally {
150                     Log.endSession();
151                 }
152             }
153 
154             @Override
onServiceDisconnected(ComponentName componentName)155             public void onServiceDisconnected(ComponentName componentName) {
156                 Log.startSession("CRSC.oSD");
157                 try {
158                     synchronized (mTelecomLock) {
159                         finishCallRedirection();
160                     }
161                 } finally {
162                     Log.endSession();
163                 }
164             }
165 
166             @Override
onNullBinding(ComponentName componentName)167             public void onNullBinding(ComponentName componentName) {
168                 // Make sure we unbind the service if onBind returns null
169                 Log.startSession("CRSC.oNB");
170                 try {
171                     synchronized (mTelecomLock) {
172                         finishCallRedirection();
173                     }
174                 } finally {
175                     Log.endSession();
176                 }
177             }
178         }
179 
180         private class CallRedirectionAdapter extends ICallRedirectionAdapter.Stub {
181             @Override
cancelCall()182             public void cancelCall() {
183                 Log.startSession("CRA.cC");
184                 long token = Binder.clearCallingIdentity();
185                 try {
186                     synchronized (mTelecomLock) {
187                         Log.d(this, "Received cancelCall from " +  mServiceType + " call"
188                                 + " redirection service");
189                         mShouldCancelCall = true;
190                         finishCallRedirection();
191                     }
192                 } finally {
193                     Binder.restoreCallingIdentity(token);
194                     Log.endSession();
195                 }
196             }
197 
198             @Override
placeCallUnmodified()199             public void placeCallUnmodified() {
200                 Log.startSession("CRA.pCU");
201                 long token = Binder.clearCallingIdentity();
202                 try {
203                     synchronized (mTelecomLock) {
204                         Log.d(this, "Received placeCallUnmodified from " +  mServiceType + " call"
205                                 + " redirection service");
206                         finishCallRedirection();
207                     }
208                 } finally {
209                     Binder.restoreCallingIdentity(token);
210                     Log.endSession();
211                 }
212             }
213 
214             @Override
redirectCall(Uri gatewayUri, PhoneAccountHandle targetPhoneAccount, boolean confirmFirst)215             public void redirectCall(Uri gatewayUri, PhoneAccountHandle targetPhoneAccount,
216                                      boolean confirmFirst) {
217                 Log.startSession("CRA.rC");
218                 long token = Binder.clearCallingIdentity();
219                 try {
220                     synchronized (mTelecomLock) {
221                         mRedirectionGatewayInfo = mCallRedirectionProcessorHelper
222                                 .getGatewayInfoFromGatewayUri(mComponentName.getPackageName(),
223                                         gatewayUri, mDestinationUri, mPostDialDigits);
224                         mPhoneAccountHandle = targetPhoneAccount;
225                         // If carrier redirects call, we should skip to notify users about
226                         // the user-defined call redirection service.
227                         mUiAction = (confirmFirst && mServiceType.equals(SERVICE_TYPE_USER_DEFINED)
228                                 && mAllowInteractiveResponse)
229                                 ? UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM : UI_TYPE_NO_ACTION;
230                         Log.d(this, "Received redirectCall with [gatewayUri]"
231                                 + Log.pii(gatewayUri) + " [phoneAccountHandle]"
232                                 + mPhoneAccountHandle + "[confirmFirst]" + confirmFirst + " from "
233                                 + mServiceType + " call redirection service");
234                         finishCallRedirection();
235                     }
236                 } finally {
237                     Binder.restoreCallingIdentity(token);
238                     Log.endSession();
239                 }
240             }
241         }
242     }
243 
244     private final Context mContext;
245     private final CallsManager mCallsManager;
246     private final Call mCall;
247     private final boolean mAllowInteractiveResponse;
248     private GatewayInfo mRedirectionGatewayInfo;
249     private final boolean mSpeakerphoneOn;
250     private final int mVideoState;
251     private final Timeouts.Adapter mTimeoutsAdapter;
252     private final TelecomSystem.SyncRoot mTelecomLock;
253     private final Handler mHandler = new Handler(Looper.getMainLooper());
254 
255     private CallRedirectionAttempt mAttempt;
256     private CallRedirectionProcessorHelper mCallRedirectionProcessorHelper;
257 
258     public static final String SERVICE_TYPE_CARRIER = "carrier";
259     public static final String SERVICE_TYPE_USER_DEFINED = "user_defined";
260     public static final String UI_TYPE_NO_ACTION = "no_action";
261     public static final String UI_TYPE_USER_DEFINED_TIMEOUT = "user_defined_timeout";
262     public static final String UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM
263             = "user_defined_ask_for_confirm";
264 
265     private PhoneAccountHandle mPhoneAccountHandle;
266     private Uri mDestinationUri;
267     /**
268      * Try to send the implemented service with processed destination uri by formatting it to E.164
269      * and removing post dial digits.
270      */
271     private Uri mProcessedDestinationUri;
272 
273     /**
274      * The post dial digits which were removed from {@link #mDestinationUri} when determining
275      * {@link #mProcessedDestinationUri}.
276      */
277     private String mPostDialDigits;
278 
279     /**
280      * Indicates if Telecom should cancel the call when the whole call redirection finishes.
281      */
282     private boolean mShouldCancelCall = false;
283     /**
284      * Indicates Telecom should handle different types of UI if need.
285      */
286     private String mUiAction = UI_TYPE_NO_ACTION;
287     /**
288      * Indicates if Telecom is waiting for a callback from a user-defined
289      * {@link CallRedirectionService}.
290      */
291     private boolean mIsUserDefinedRedirectionPending = false;
292     /**
293      * Indicates if Telecom is waiting for a callback from a carrier
294      * {@link CallRedirectionService}.
295      */
296     private boolean mIsCarrierRedirectionPending = false;
297 
CallRedirectionProcessor( Context context, CallsManager callsManager, Call call, Uri handle, PhoneAccountRegistrar phoneAccountRegistrar, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState)298     public CallRedirectionProcessor(
299             Context context,
300             CallsManager callsManager,
301             Call call,
302             Uri handle,
303             PhoneAccountRegistrar phoneAccountRegistrar,
304             GatewayInfo gatewayInfo,
305             boolean speakerphoneOn,
306             int videoState) {
307         mContext = context;
308         mCallsManager = callsManager;
309         mCall = call;
310         mDestinationUri = handle;
311         mPhoneAccountHandle = call.getTargetPhoneAccount();
312         mRedirectionGatewayInfo = gatewayInfo;
313         mSpeakerphoneOn = speakerphoneOn;
314         mVideoState = videoState;
315         mTimeoutsAdapter = callsManager.getTimeoutsAdapter();
316         mTelecomLock = callsManager.getLock();
317         /**
318          * The current rule to decide whether the implemented {@link CallRedirectionService} should
319          * allow interactive responses with users is only based on whether it is in car mode.
320          */
321         mAllowInteractiveResponse = !callsManager.getSystemStateHelper().isCarModeOrProjectionActive();
322         mCallRedirectionProcessorHelper = new CallRedirectionProcessorHelper(
323                 context, callsManager, phoneAccountRegistrar);
324         mProcessedDestinationUri = mCallRedirectionProcessorHelper.formatNumberForRedirection(
325                 mDestinationUri);
326         mPostDialDigits = mCallRedirectionProcessorHelper.getPostDialDigits(mDestinationUri);
327     }
328 
329     @Override
onCallRedirectionComplete(Call call)330     public void onCallRedirectionComplete(Call call) {
331         // synchronized on mTelecomLock to enter into Telecom.
332         mHandler.post(new Runnable("CRP.oCRC", mTelecomLock) {
333             @Override
334             public void loggedRun() {
335                 if (mIsUserDefinedRedirectionPending) {
336                     Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_USER);
337                     mIsUserDefinedRedirectionPending = false;
338                     if (mShouldCancelCall) {
339                         mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri,
340                                 mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn,
341                                 mVideoState, mShouldCancelCall, mUiAction);
342                     } else {
343                         performCarrierCallRedirection();
344                     }
345                 }
346                 if (mIsCarrierRedirectionPending) {
347                     Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_CARRIER);
348                     mIsCarrierRedirectionPending = false;
349                     mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri,
350                             mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn,
351                             mVideoState, mShouldCancelCall, mUiAction);
352                 }
353             }
354         }.prepare());
355     }
356 
357     /**
358      * The entry to perform call redirection of the call from (@link CallsManager)
359      */
performCallRedirection()360     public void performCallRedirection() {
361         // If the Gateway Info is set with intent, only request with carrier call redirection.
362         if (mRedirectionGatewayInfo != null) {
363             performCarrierCallRedirection();
364         } else {
365             performUserDefinedCallRedirection();
366         }
367     }
368 
performUserDefinedCallRedirection()369     private void performUserDefinedCallRedirection() {
370         Log.d(this, "performUserDefinedCallRedirection");
371         ComponentName componentName =
372                 mCallRedirectionProcessorHelper.getUserDefinedCallRedirectionService();
373         if (componentName != null) {
374             mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_USER_DEFINED);
375             mAttempt.process();
376             mIsUserDefinedRedirectionPending = true;
377             processTimeoutForCallRedirection(SERVICE_TYPE_USER_DEFINED);
378         } else {
379             Log.i(this, "There are no user-defined call redirection services installed on this"
380                     + " device.");
381             performCarrierCallRedirection();
382         }
383     }
384 
performCarrierCallRedirection()385     private void performCarrierCallRedirection() {
386         Log.d(this, "performCarrierCallRedirection");
387         ComponentName componentName =
388                 mCallRedirectionProcessorHelper.getCarrierCallRedirectionService(
389                         mPhoneAccountHandle);
390         if (componentName != null) {
391             mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_CARRIER);
392             mAttempt.process();
393             mIsCarrierRedirectionPending = true;
394             processTimeoutForCallRedirection(SERVICE_TYPE_CARRIER);
395         } else {
396             Log.i(this, "There are no carrier call redirection services installed on this"
397                     + " device.");
398             mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri,
399                     mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn, mVideoState,
400                     mShouldCancelCall, mUiAction);
401         }
402     }
403 
processTimeoutForCallRedirection(String serviceType)404     private void processTimeoutForCallRedirection(String serviceType) {
405         long timeout = serviceType.equals(SERVICE_TYPE_USER_DEFINED) ?
406             mTimeoutsAdapter.getUserDefinedCallRedirectionTimeoutMillis(
407                 mContext.getContentResolver()) : mTimeoutsAdapter
408             .getCarrierCallRedirectionTimeoutMillis(mContext.getContentResolver());
409 
410         mHandler.postDelayed(new Runnable("CRP.pTFCR", null) {
411             @Override
412             public void loggedRun() {
413                 boolean isCurrentRedirectionPending =
414                         serviceType.equals(SERVICE_TYPE_USER_DEFINED) ?
415                                 mIsUserDefinedRedirectionPending : mIsCarrierRedirectionPending;
416                 if (isCurrentRedirectionPending) {
417                     Log.i(this, serviceType + " call redirection has timed out.");
418                     Log.addEvent(mCall, serviceType.equals(SERVICE_TYPE_USER_DEFINED)
419                             ? LogUtils.Events.REDIRECTION_TIMED_OUT_USER
420                             : LogUtils.Events.REDIRECTION_TIMED_OUT_CARRIER);
421                     mAttempt.notifyTimeout();
422                     if (serviceType.equals(SERVICE_TYPE_USER_DEFINED)) {
423                         mUiAction = UI_TYPE_USER_DEFINED_TIMEOUT;
424                         mShouldCancelCall = true;
425                     }
426                     onCallRedirectionComplete(mCall);
427                 }
428             }
429         }.prepare(), timeout);
430     }
431 
432     /**
433      * Checks if Telecom can make call redirection with any available call redirection service.
434      *
435      * @return {@code true} if it can; {@code false} otherwise.
436      */
canMakeCallRedirectionWithService()437     public boolean canMakeCallRedirectionWithService() {
438         boolean canMakeCallRedirectionWithService =
439                 mCallRedirectionProcessorHelper.getUserDefinedCallRedirectionService() != null
440                         || mCallRedirectionProcessorHelper.getCarrierCallRedirectionService(
441                                 mPhoneAccountHandle) != null;
442         Log.i(this, "Can make call redirection with any available service: "
443                 + canMakeCallRedirectionWithService);
444         return canMakeCallRedirectionWithService;
445     }
446 
447     /**
448      * Returns the handler, for testing purposes.
449      */
450     @VisibleForTesting
getHandler()451     public Handler getHandler() {
452         return mHandler;
453     }
454 
455     /**
456      * Set CallRedirectionProcessorHelper for testing purposes.
457      */
458     @VisibleForTesting
setCallRedirectionServiceHelper( CallRedirectionProcessorHelper callRedirectionProcessorHelper)459     public void setCallRedirectionServiceHelper(
460             CallRedirectionProcessorHelper callRedirectionProcessorHelper) {
461         mCallRedirectionProcessorHelper = callRedirectionProcessorHelper;
462     }
463 }
464