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