• 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 
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);
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      * Indicates if Telecom should cancel the call when the whole call redirection finishes.
251      */
252     private boolean mShouldCancelCall = false;
253     /**
254      * Indicates Telecom should handle different types of UI if need.
255      */
256     private String mUiAction = UI_TYPE_NO_ACTION;
257     /**
258      * Indicates if Telecom is waiting for a callback from a user-defined
259      * {@link CallRedirectionService}.
260      */
261     private boolean mIsUserDefinedRedirectionPending = false;
262     /**
263      * Indicates if Telecom is waiting for a callback from a carrier
264      * {@link CallRedirectionService}.
265      */
266     private boolean mIsCarrierRedirectionPending = false;
267 
CallRedirectionProcessor( Context context, CallsManager callsManager, Call call, Uri handle, PhoneAccountRegistrar phoneAccountRegistrar, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState)268     public CallRedirectionProcessor(
269             Context context,
270             CallsManager callsManager,
271             Call call,
272             Uri handle,
273             PhoneAccountRegistrar phoneAccountRegistrar,
274             GatewayInfo gatewayInfo,
275             boolean speakerphoneOn,
276             int videoState) {
277         mContext = context;
278         mCallsManager = callsManager;
279         mCall = call;
280         mDestinationUri = handle;
281         mPhoneAccountHandle = call.getTargetPhoneAccount();
282         mRedirectionGatewayInfo = gatewayInfo;
283         mSpeakerphoneOn = speakerphoneOn;
284         mVideoState = videoState;
285         mTimeoutsAdapter = callsManager.getTimeoutsAdapter();
286         mTelecomLock = callsManager.getLock();
287         /**
288          * The current rule to decide whether the implemented {@link CallRedirectionService} should
289          * allow interactive responses with users is only based on whether it is in car mode.
290          */
291         mAllowInteractiveResponse = !callsManager.getSystemStateHelper().isCarMode();
292         mCallRedirectionProcessorHelper = new CallRedirectionProcessorHelper(
293                 context, callsManager, phoneAccountRegistrar);
294         mProcessedDestinationUri = mCallRedirectionProcessorHelper.formatNumberForRedirection(
295                 mDestinationUri);
296     }
297 
298     @Override
onCallRedirectionComplete(Call call)299     public void onCallRedirectionComplete(Call call) {
300         // synchronized on mTelecomLock to enter into Telecom.
301         mHandler.post(new Runnable("CRP.oCRC", mTelecomLock) {
302             @Override
303             public void loggedRun() {
304                 if (mIsUserDefinedRedirectionPending) {
305                     Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_USER);
306                     mIsUserDefinedRedirectionPending = false;
307                     if (mShouldCancelCall) {
308                         mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri,
309                                 mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn,
310                                 mVideoState, mShouldCancelCall, mUiAction);
311                     } else {
312                         performCarrierCallRedirection();
313                     }
314                 }
315                 if (mIsCarrierRedirectionPending) {
316                     Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_CARRIER);
317                     mIsCarrierRedirectionPending = false;
318                     mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri,
319                             mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn,
320                             mVideoState, mShouldCancelCall, mUiAction);
321                 }
322             }
323         }.prepare());
324     }
325 
326     /**
327      * The entry to perform call redirection of the call from (@link CallsManager)
328      */
performCallRedirection()329     public void performCallRedirection() {
330         // If the Gateway Info is set with intent, only request with carrier call redirection.
331         if (mRedirectionGatewayInfo != null) {
332             performCarrierCallRedirection();
333         } else {
334             performUserDefinedCallRedirection();
335         }
336     }
337 
performUserDefinedCallRedirection()338     private void performUserDefinedCallRedirection() {
339         Log.d(this, "performUserDefinedCallRedirection");
340         ComponentName componentName =
341                 mCallRedirectionProcessorHelper.getUserDefinedCallRedirectionService();
342         if (componentName != null) {
343             mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_USER_DEFINED);
344             mAttempt.process();
345             mIsUserDefinedRedirectionPending = true;
346             processTimeoutForCallRedirection(SERVICE_TYPE_USER_DEFINED);
347         } else {
348             Log.i(this, "There are no user-defined call redirection services installed on this"
349                     + " device.");
350             performCarrierCallRedirection();
351         }
352     }
353 
performCarrierCallRedirection()354     private void performCarrierCallRedirection() {
355         Log.d(this, "performCarrierCallRedirection");
356         ComponentName componentName =
357                 mCallRedirectionProcessorHelper.getCarrierCallRedirectionService(
358                         mPhoneAccountHandle);
359         if (componentName != null) {
360             mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_CARRIER);
361             mAttempt.process();
362             mIsCarrierRedirectionPending = true;
363             processTimeoutForCallRedirection(SERVICE_TYPE_CARRIER);
364         } else {
365             Log.i(this, "There are no carrier call redirection services installed on this"
366                     + " device.");
367             mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri,
368                     mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn, mVideoState,
369                     mShouldCancelCall, mUiAction);
370         }
371     }
372 
processTimeoutForCallRedirection(String serviceType)373     private void processTimeoutForCallRedirection(String serviceType) {
374         long timeout = serviceType.equals(SERVICE_TYPE_USER_DEFINED) ?
375             mTimeoutsAdapter.getUserDefinedCallRedirectionTimeoutMillis(
376                 mContext.getContentResolver()) : mTimeoutsAdapter
377             .getCarrierCallRedirectionTimeoutMillis(mContext.getContentResolver());
378 
379         mHandler.postDelayed(new Runnable("CRP.pTFCR", null) {
380             @Override
381             public void loggedRun() {
382                 boolean isCurrentRedirectionPending =
383                         serviceType.equals(SERVICE_TYPE_USER_DEFINED) ?
384                                 mIsUserDefinedRedirectionPending : mIsCarrierRedirectionPending;
385                 if (isCurrentRedirectionPending) {
386                     Log.i(this, serviceType + " call redirection has timed out.");
387                     Log.addEvent(mCall, serviceType.equals(SERVICE_TYPE_USER_DEFINED)
388                             ? LogUtils.Events.REDIRECTION_TIMED_OUT_USER
389                             : LogUtils.Events.REDIRECTION_TIMED_OUT_CARRIER);
390                     if (serviceType.equals(SERVICE_TYPE_USER_DEFINED)) {
391                         mUiAction = UI_TYPE_USER_DEFINED_TIMEOUT;
392                         mShouldCancelCall = true;
393                     }
394                     onCallRedirectionComplete(mCall);
395                 }
396             }
397         }.prepare(), timeout);
398     }
399 
400     /**
401      * Checks if Telecom can make call redirection with any available call redirection service.
402      *
403      * @return {@code true} if it can; {@code false} otherwise.
404      */
canMakeCallRedirectionWithService()405     public boolean canMakeCallRedirectionWithService() {
406         boolean canMakeCallRedirectionWithService =
407                 mCallRedirectionProcessorHelper.getUserDefinedCallRedirectionService() != null
408                         || mCallRedirectionProcessorHelper.getCarrierCallRedirectionService(
409                                 mPhoneAccountHandle) != null;
410         Log.i(this, "Can make call redirection with any available service: "
411                 + canMakeCallRedirectionWithService);
412         return canMakeCallRedirectionWithService;
413     }
414 
415     /**
416      * Returns the handler, for testing purposes.
417      */
418     @VisibleForTesting
getHandler()419     public Handler getHandler() {
420         return mHandler;
421     }
422 
423     /**
424      * Set CallRedirectionProcessorHelper for testing purposes.
425      */
426     @VisibleForTesting
setCallRedirectionServiceHelper( CallRedirectionProcessorHelper callRedirectionProcessorHelper)427     public void setCallRedirectionServiceHelper(
428             CallRedirectionProcessorHelper callRedirectionProcessorHelper) {
429         mCallRedirectionProcessorHelper = callRedirectionProcessorHelper;
430     }
431 }
432