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