Loading src/com/android/server/telecom/CallsManager.java +63 −1 Original line number Diff line number Diff line Loading @@ -299,6 +299,7 @@ public class CallsManager extends Call.ListenerBase new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1)); private final HeadsetMediaButton mHeadsetMediaButton; private final WiredHeadsetManager mWiredHeadsetManager; private final SystemStateHelper mSystemStateHelper; private final BluetoothRouteManager mBluetoothRouteManager; private final DockManager mDockManager; private final TtyManager mTtyManager; Loading Loading @@ -436,6 +437,7 @@ public class CallsManager extends Call.ListenerBase mMissedCallNotifier = missedCallNotifier; StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this); mWiredHeadsetManager = wiredHeadsetManager; mSystemStateHelper = systemStateHelper; mDefaultDialerCache = defaultDialerCache; mBluetoothRouteManager = bluetoothManager; mDockManager = new DockManager(context); Loading Loading @@ -1606,6 +1608,57 @@ public class CallsManager extends Call.ListenerBase return targetPhoneAccount != null && targetPhoneAccount.isSelfManaged(); } public void onCallRedirectionComplete(Call call, Uri handle, PhoneAccountHandle phoneAccountHandle, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState, boolean shouldCancelCall, String uiAction) { Log.i(this, "onCallRedirectionComplete for Call %s with handle %s" + " and phoneAccountHandle %s", call, handle, phoneAccountHandle); boolean endEarly = false; String disconnectReason = ""; if (shouldCancelCall) { Log.w(this, "onCallRedirectionComplete: call is canceled"); endEarly = true; disconnectReason = "Canceled from Call Redirection Service"; // TODO show UI uiAction is CallRedirectionProcessor#UI_TYPE_USER_DEFINED_TIMEOUT } else if (handle == null) { Log.w(this, "onCallRedirectionComplete: handle is null"); endEarly = true; disconnectReason = "Null handle from Call Redirection Service"; } else if (phoneAccountHandle == null) { Log.w(this, "onCallRedirectionComplete: phoneAccountHandle is null"); endEarly = true; disconnectReason = "Null phoneAccountHandle from Call Redirection Service"; } else if (mPhoneNumberUtilsAdapter.isPotentialLocalEmergencyNumber(mContext, handle.getSchemeSpecificPart())) { Log.w(this, "onCallRedirectionComplete: emergency number %s is redirected from Call" + " Redirection Service", handle.getSchemeSpecificPart()); endEarly = true; disconnectReason = "Emergency number is redirected from Call Redirection Service"; } if (endEarly) { if (call != null) { call.disconnect(disconnectReason); } return; } // If this call is already disconnected then we have nothing more to do. if (call.isDisconnected()) { Log.w(this, "onCallRedirectionComplete: Call has already been disconnected," + " ignore the call redirection %s", call); return; } // TODO show UI uiAction is CallRedirectionProcessor#UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM call.setTargetPhoneAccount(phoneAccountHandle); placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState); } /** * Attempts to issue/connect the specified call. * Loading Loading @@ -2590,7 +2643,8 @@ public class CallsManager extends Call.ListenerBase * * @return The {@link PhoneAccountRegistrar}. */ PhoneAccountRegistrar getPhoneAccountRegistrar() { @VisibleForTesting public PhoneAccountRegistrar getPhoneAccountRegistrar() { return mPhoneAccountRegistrar; } Loading Loading @@ -3387,6 +3441,14 @@ public class CallsManager extends Call.ListenerBase return mLock; } public Timeouts.Adapter getTimeoutsAdapter() { return mTimeoutsAdapter; } public SystemStateHelper getSystemStateHelper() { return mSystemStateHelper; } private void reloadMissedCallsOfUser(UserHandle userHandle) { mMissedCallNotifier.reloadFromDatabase(mCallerInfoLookupHelper, new MissedCallNotifier.CallInfoFactory(), userHandle); Loading src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java +28 −1 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import android.telephony.DisconnectCause; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.server.telecom.callredirection.CallRedirectionProcessor; // TODO: Needed for move to system service: import com.android.internal.R; Loading Loading @@ -240,6 +241,8 @@ public class NewOutgoingCallIntentBroadcaster { boolean callImmediately = false; // True for all managed calls, false for self-managed calls. boolean sendNewOutgoingCallBroadcast = true; // True for requesting call redirection, false for not requesting it. boolean requestCallRedirection = true; Uri callingAddress = handle; if (!isSelfManaged) { Loading Loading @@ -294,6 +297,7 @@ public class NewOutgoingCallIntentBroadcaster { // Self-managed call. callImmediately = true; sendNewOutgoingCallBroadcast = false; requestCallRedirection = false; Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call."); } Loading @@ -312,10 +316,33 @@ public class NewOutgoingCallIntentBroadcaster { // initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra. } boolean callRedirectionWithService = false; if (requestCallRedirection) { CallRedirectionProcessor callRedirectionProcessor = new CallRedirectionProcessor( mContext, mCallsManager, mCall, callingAddress, mCallsManager.getPhoneAccountRegistrar(), getGateWayInfoFromIntent(intent, handle), intent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false), intent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_AUDIO_ONLY)); /** * If there is an available {@link android.telecom.CallRedirectionService}, use the * {@link CallRedirectionProcessor} to perform call redirection instead of using * broadcasting. */ callRedirectionWithService = callRedirectionProcessor .canMakeCallRedirectionWithService(); if (callRedirectionWithService) { callRedirectionProcessor.performCallRedirection(); } } if (sendNewOutgoingCallBroadcast) { UserHandle targetUser = mCall.getInitiatingUser(); Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser); broadcastIntent(intent, number, !callImmediately, targetUser); broadcastIntent(intent, number, !callImmediately && !callRedirectionWithService, targetUser); } return DisconnectCause.NOT_DISCONNECTED; } Loading src/com/android/server/telecom/Timeouts.java +2 −2 Original line number Diff line number Diff line Loading @@ -187,7 +187,7 @@ public final class Timeouts { */ public static long getUserDefinedCallRedirectionTimeoutMillis(ContentResolver contentResolver) { return get(contentResolver, "user_defined_call_redirection_timeout", 3000L /* 3 seconds */); 5000L /* 5 seconds */); } /** Loading @@ -196,6 +196,6 @@ public final class Timeouts { * @param contentResolver The content resolved. */ public static long getCarrierCallRedirectionTimeoutMillis(ContentResolver contentResolver) { return get(contentResolver, "carrier_call_redirection_timeout", 3000L /* 3 seconds */); return get(contentResolver, "carrier_call_redirection_timeout", 5000L /* 5 seconds */); } } src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java +99 −86 Original line number Diff line number Diff line Loading @@ -16,26 +16,22 @@ package com.android.server.telecom.callredirection; import android.Manifest; import android.app.AppOpsManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.UserHandle; import android.telecom.CallRedirectionService; import android.telecom.GatewayInfo; import android.telecom.Log; import android.telecom.Logging.Runnable; import android.telecom.PhoneAccountHandle; import android.telephony.CarrierConfigManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telecom.ICallRedirectionAdapter; import com.android.internal.telecom.ICallRedirectionService; Loading @@ -46,8 +42,6 @@ import com.android.server.telecom.PhoneAccountRegistrar; import com.android.server.telecom.TelecomSystem; import com.android.server.telecom.Timeouts; import java.util.List; /** * A single instance of call redirection processor that handles the call redirection with * user-defined {@link CallRedirectionService} and carrier {@link CallRedirectionService} for a Loading Loading @@ -98,10 +92,14 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { private void onServiceBound(ICallRedirectionService service) { mService = service; try { mService.placeCall(new CallRedirectionAdapter(), mHandle, mPhoneAccountHandle); mHandle = mCallRedirectionProcessorHelper.formatNumberForRedirection(mHandle); // Telecom does not perform user interactions for carrier call redirection. mService.placeCall(new CallRedirectionAdapter(), mHandle, mPhoneAccountHandle, mAllowInteractiveResponse && mServiceType.equals(SERVICE_TYPE_USER_DEFINED)); Log.addEvent(mCall, mServiceType.equals(SERVICE_TYPE_USER_DEFINED) ? LogUtils.Events.REDIRECTION_SENT_USER : LogUtils.Events.REDIRECTION_SENT_CARRIER); : LogUtils.Events.REDIRECTION_SENT_CARRIER, mComponentName); Log.d(this, "Requested placeCall with [handle]" + Log.pii(mHandle) + " [phoneAccountHandle]" + mPhoneAccountHandle); } catch (RemoteException e) { Loading Loading @@ -189,16 +187,20 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { } @Override public void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount) { public void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount, boolean confirmFirst) { Log.startSession("CRA.rC"); long token = Binder.clearCallingIdentity(); try { synchronized (mTelecomLock) { mHandle = handle; mPhoneAccountHandle = targetPhoneAccount; mUiAction = (confirmFirst && mServiceType.equals(SERVICE_TYPE_USER_DEFINED) && mAllowInteractiveResponse) ? UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM : mUiAction; Log.d(this, "Received redirectCall with [handle]" + Log.pii(mHandle) + " [phoneAccountHandle]" + mPhoneAccountHandle + " from " + mServiceType + " call" + " redirection service"); + mServiceType + " call redirection service"); finishCallRedirection(); } } finally { Loading @@ -212,14 +214,23 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { private final Context mContext; private final CallsManager mCallsManager; private final Call mCall; private final PhoneAccountRegistrar mPhoneAccountRegistrar; private final boolean mAllowInteractiveResponse; private final GatewayInfo mGatewayInfo; private final boolean mSpeakerphoneOn; private final int mVideoState; private final Timeouts.Adapter mTimeoutsAdapter; private final TelecomSystem.SyncRoot mTelecomLock; private final Handler mHandler = new Handler(Looper.getMainLooper()); private CallRedirectionAttempt mAttempt; private CallRedirectionProcessorHelper mCallRedirectionProcessorHelper; public static final String SERVICE_TYPE_CARRIER = "carrier"; public static final String SERVICE_TYPE_USER_DEFINED = "user_defined"; public static final String UI_TYPE_NO_ACTION = "no_action"; public static final String UI_TYPE_USER_DEFINED_TIMEOUT = "user_defined_timeout"; public static final String UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM = "user_defined_ask_for_confirm"; private PhoneAccountHandle mPhoneAccountHandle; private Uri mHandle; Loading @@ -228,6 +239,10 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { * Indicates if Telecom should cancel the call when the whole call redirection finishes. */ private boolean mShouldCancelCall = false; /** * Indicates Telecom should handle different types of UI if need. */ private String mUiAction = UI_TYPE_NO_ACTION; /** * Indicates if Telecom is waiting for a callback from a user-defined * {@link CallRedirectionService}. Loading @@ -243,19 +258,28 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { Context context, CallsManager callsManager, Call call, PhoneAccountRegistrar phoneAccountRegistrar, Uri handle, PhoneAccountHandle phoneAccountHandle, Timeouts.Adapter timeoutsAdapter, TelecomSystem.SyncRoot lock) { PhoneAccountRegistrar phoneAccountRegistrar, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState) { mContext = context; mCallsManager = callsManager; mCall = call; mPhoneAccountRegistrar = phoneAccountRegistrar; mHandle = handle; mPhoneAccountHandle = phoneAccountHandle; mTimeoutsAdapter = timeoutsAdapter; mTelecomLock = lock; mPhoneAccountHandle = call.getTargetPhoneAccount(); mGatewayInfo = gatewayInfo; mSpeakerphoneOn = speakerphoneOn; mVideoState = videoState; mTimeoutsAdapter = callsManager.getTimeoutsAdapter(); mTelecomLock = callsManager.getLock(); /** * The current rule to decide whether the implemented {@link CallRedirectionService} should * allow interactive responses with users is only based on whether it is in car mode. */ mAllowInteractiveResponse = !callsManager.getSystemStateHelper().isCarMode(); mCallRedirectionProcessorHelper = new CallRedirectionProcessorHelper( context, callsManager, phoneAccountRegistrar); } @Override Loading @@ -264,11 +288,15 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { mHandler.post(new Runnable("CRP.oCRC", mTelecomLock) { @Override public void loggedRun() { mHandle = mCallRedirectionProcessorHelper.processNumberWhenRedirectionComplete( mHandle); if (mIsUserDefinedRedirectionPending) { Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_USER); mIsUserDefinedRedirectionPending = false; if (mShouldCancelCall) { // TODO mCallsManager.onCallRedirectionComplete mCallsManager.onCallRedirectionComplete(mCall, mHandle, mPhoneAccountHandle, mGatewayInfo, mSpeakerphoneOn, mVideoState, mShouldCancelCall, mUiAction); } else { performCarrierCallRedirection(); } Loading @@ -276,23 +304,33 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { if (mIsCarrierRedirectionPending) { Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_CARRIER); mIsCarrierRedirectionPending = false; // TODO mCallsManager.onCallRedirectionComplete mCallsManager.onCallRedirectionComplete(mCall, mHandle, mPhoneAccountHandle, mGatewayInfo, mSpeakerphoneOn, mVideoState, mShouldCancelCall, mUiAction); } } }.prepare()); } /* /** * The entry to perform call redirection of the call from (@link CallsManager) */ public void performCallRedirection() { // If the Gateway Info is set with intent, do not perform call redirection. if (mGatewayInfo != null) { mCallsManager.onCallRedirectionComplete(mCall, mHandle, mPhoneAccountHandle, mGatewayInfo, mSpeakerphoneOn, mVideoState, mShouldCancelCall, mUiAction); } else { mCallRedirectionProcessorHelper.storePostDialDigits(mHandle); performUserDefinedCallRedirection(); } } private void performUserDefinedCallRedirection() { Log.d(this, "performUserDefinedCallRedirection"); ComponentName componentName = getUserDefinedCallRedirectionService(mContext); if (componentName != null && canBindToCallRedirectionService(mContext, componentName)) { ComponentName componentName = mCallRedirectionProcessorHelper.getUserDefinedCallRedirectionService(); if (componentName != null) { mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_USER_DEFINED); mAttempt.process(); mIsUserDefinedRedirectionPending = true; Loading @@ -306,9 +344,10 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { private void performCarrierCallRedirection() { Log.d(this, "performCarrierCallRedirection"); ComponentName componentName = getCarrierCallRedirectionService( mContext, mPhoneAccountHandle); if (componentName != null && canBindToCallRedirectionService(mContext, componentName)) { ComponentName componentName = mCallRedirectionProcessorHelper.getCarrierCallRedirectionService( mPhoneAccountHandle); if (componentName != null) { mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_CARRIER); mAttempt.process(); mIsCarrierRedirectionPending = true; Loading @@ -316,7 +355,9 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { } else { Log.i(this, "There are no carrier call redirection services installed on this" + " device."); // TODO return to CallsManager.onCallRedirectionComplete mCallsManager.onCallRedirectionComplete(mCall, mHandle, mPhoneAccountHandle, mGatewayInfo, mSpeakerphoneOn, mVideoState, mShouldCancelCall, mUiAction); } } Loading @@ -333,77 +374,49 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { serviceType.equals(SERVICE_TYPE_USER_DEFINED) ? mIsUserDefinedRedirectionPending : mIsCarrierRedirectionPending; if (isCurrentRedirectionPending) { Log.i(CallRedirectionProcessor.this, serviceType + "call redirection has timed out."); Log.i(this, serviceType + " call redirection has timed out."); Log.addEvent(mCall, serviceType.equals(SERVICE_TYPE_USER_DEFINED) ? LogUtils.Events.REDIRECTION_TIMED_OUT_USER : LogUtils.Events.REDIRECTION_TIMED_OUT_CARRIER); if (serviceType.equals(SERVICE_TYPE_USER_DEFINED)) { mUiAction = UI_TYPE_USER_DEFINED_TIMEOUT; mShouldCancelCall = true; } onCallRedirectionComplete(mCall); } } }.prepare(), timeout); } private ComponentName getUserDefinedCallRedirectionService(Context context) { // TODO get service component name from settings default value: // android.provider.Settings#CALL_REDIRECTION_DEFAULT_APPLICATION return null; } private ComponentName getCarrierCallRedirectionService(Context context, PhoneAccountHandle targetPhoneAccountHandle) { CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); if (configManager == null) { Log.i(this, "Cannot get CarrierConfigManager."); return null; } PersistableBundle pb = configManager.getConfigForSubId(mPhoneAccountRegistrar .getSubscriptionIdForPhoneAccount(targetPhoneAccountHandle)); if (pb == null) { Log.i(this, "Cannot get PersistableBundle."); return null; } String componentNameString = pb.getString( CarrierConfigManager.KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING); return new ComponentName(context, componentNameString); } private boolean canBindToCallRedirectionService(Context context, ComponentName componentName) { Intent intent = new Intent(CallRedirectionService.SERVICE_INTERFACE); intent.setComponent(componentName); List<ResolveInfo> entries = mContext.getPackageManager().queryIntentServicesAsUser( intent, 0, mCallsManager.getCurrentUserHandle().getIdentifier()); if (entries.isEmpty()) { Log.i(this, "There are no call redirection services installed on this device."); return false; } else if (entries.size() != 1) { Log.i(this, "There are multiple call redirection services installed on this device."); return false; } else { ResolveInfo entry = entries.get(0); if (entry.serviceInfo.permission == null || !entry.serviceInfo.permission.equals( Manifest.permission.BIND_CALL_REDIRECTION_SERVICE)) { Log.w(this, "CallRedirectionService must require BIND_CALL_REDIRECTION_SERVICE" + " permission: " + entry.serviceInfo.packageName); return false; } AppOpsManager appOps = (AppOpsManager) context.getSystemService( Context.APP_OPS_SERVICE); if (appOps.noteOp(AppOpsManager.OP_PROCESS_OUTGOING_CALLS, Binder.getCallingUid(), entry.serviceInfo.packageName) != AppOpsManager.MODE_ALLOWED) { Log.w(this, "App Ops does not allow " + entry.serviceInfo.packageName); return false; } } return true; /** * Checks if Telecom can make call redirection with any available call redirection service. * * @return {@code true} if it can; {@code false} otherwise. */ public boolean canMakeCallRedirectionWithService() { boolean canMakeCallRedirectionWithService = mCallRedirectionProcessorHelper.getUserDefinedCallRedirectionService() != null || mCallRedirectionProcessorHelper.getCarrierCallRedirectionService( mPhoneAccountHandle) != null; Log.w(this, "Can make call redirection with any available service: " + canMakeCallRedirectionWithService); return canMakeCallRedirectionWithService; } /** * Returns the handler for testing purposes. * Returns the handler, for testing purposes. */ @VisibleForTesting public Handler getHandler() { return mHandler; } /** * Set CallRedirectionProcessorHelper for testing purposes. */ @VisibleForTesting public void setCallRedirectionServiceHelper( CallRedirectionProcessorHelper callRedirectionProcessorHelper) { mCallRedirectionProcessorHelper = callRedirectionProcessorHelper; } } src/com/android/server/telecom/callredirection/CallRedirectionProcessorHelper.java 0 → 100644 +199 −0 File added.Preview size limit exceeded, changes collapsed. Show changes Loading
src/com/android/server/telecom/CallsManager.java +63 −1 Original line number Diff line number Diff line Loading @@ -299,6 +299,7 @@ public class CallsManager extends Call.ListenerBase new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1)); private final HeadsetMediaButton mHeadsetMediaButton; private final WiredHeadsetManager mWiredHeadsetManager; private final SystemStateHelper mSystemStateHelper; private final BluetoothRouteManager mBluetoothRouteManager; private final DockManager mDockManager; private final TtyManager mTtyManager; Loading Loading @@ -436,6 +437,7 @@ public class CallsManager extends Call.ListenerBase mMissedCallNotifier = missedCallNotifier; StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this); mWiredHeadsetManager = wiredHeadsetManager; mSystemStateHelper = systemStateHelper; mDefaultDialerCache = defaultDialerCache; mBluetoothRouteManager = bluetoothManager; mDockManager = new DockManager(context); Loading Loading @@ -1606,6 +1608,57 @@ public class CallsManager extends Call.ListenerBase return targetPhoneAccount != null && targetPhoneAccount.isSelfManaged(); } public void onCallRedirectionComplete(Call call, Uri handle, PhoneAccountHandle phoneAccountHandle, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState, boolean shouldCancelCall, String uiAction) { Log.i(this, "onCallRedirectionComplete for Call %s with handle %s" + " and phoneAccountHandle %s", call, handle, phoneAccountHandle); boolean endEarly = false; String disconnectReason = ""; if (shouldCancelCall) { Log.w(this, "onCallRedirectionComplete: call is canceled"); endEarly = true; disconnectReason = "Canceled from Call Redirection Service"; // TODO show UI uiAction is CallRedirectionProcessor#UI_TYPE_USER_DEFINED_TIMEOUT } else if (handle == null) { Log.w(this, "onCallRedirectionComplete: handle is null"); endEarly = true; disconnectReason = "Null handle from Call Redirection Service"; } else if (phoneAccountHandle == null) { Log.w(this, "onCallRedirectionComplete: phoneAccountHandle is null"); endEarly = true; disconnectReason = "Null phoneAccountHandle from Call Redirection Service"; } else if (mPhoneNumberUtilsAdapter.isPotentialLocalEmergencyNumber(mContext, handle.getSchemeSpecificPart())) { Log.w(this, "onCallRedirectionComplete: emergency number %s is redirected from Call" + " Redirection Service", handle.getSchemeSpecificPart()); endEarly = true; disconnectReason = "Emergency number is redirected from Call Redirection Service"; } if (endEarly) { if (call != null) { call.disconnect(disconnectReason); } return; } // If this call is already disconnected then we have nothing more to do. if (call.isDisconnected()) { Log.w(this, "onCallRedirectionComplete: Call has already been disconnected," + " ignore the call redirection %s", call); return; } // TODO show UI uiAction is CallRedirectionProcessor#UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM call.setTargetPhoneAccount(phoneAccountHandle); placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState); } /** * Attempts to issue/connect the specified call. * Loading Loading @@ -2590,7 +2643,8 @@ public class CallsManager extends Call.ListenerBase * * @return The {@link PhoneAccountRegistrar}. */ PhoneAccountRegistrar getPhoneAccountRegistrar() { @VisibleForTesting public PhoneAccountRegistrar getPhoneAccountRegistrar() { return mPhoneAccountRegistrar; } Loading Loading @@ -3387,6 +3441,14 @@ public class CallsManager extends Call.ListenerBase return mLock; } public Timeouts.Adapter getTimeoutsAdapter() { return mTimeoutsAdapter; } public SystemStateHelper getSystemStateHelper() { return mSystemStateHelper; } private void reloadMissedCallsOfUser(UserHandle userHandle) { mMissedCallNotifier.reloadFromDatabase(mCallerInfoLookupHelper, new MissedCallNotifier.CallInfoFactory(), userHandle); Loading
src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java +28 −1 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import android.telephony.DisconnectCause; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.server.telecom.callredirection.CallRedirectionProcessor; // TODO: Needed for move to system service: import com.android.internal.R; Loading Loading @@ -240,6 +241,8 @@ public class NewOutgoingCallIntentBroadcaster { boolean callImmediately = false; // True for all managed calls, false for self-managed calls. boolean sendNewOutgoingCallBroadcast = true; // True for requesting call redirection, false for not requesting it. boolean requestCallRedirection = true; Uri callingAddress = handle; if (!isSelfManaged) { Loading Loading @@ -294,6 +297,7 @@ public class NewOutgoingCallIntentBroadcaster { // Self-managed call. callImmediately = true; sendNewOutgoingCallBroadcast = false; requestCallRedirection = false; Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call."); } Loading @@ -312,10 +316,33 @@ public class NewOutgoingCallIntentBroadcaster { // initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra. } boolean callRedirectionWithService = false; if (requestCallRedirection) { CallRedirectionProcessor callRedirectionProcessor = new CallRedirectionProcessor( mContext, mCallsManager, mCall, callingAddress, mCallsManager.getPhoneAccountRegistrar(), getGateWayInfoFromIntent(intent, handle), intent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false), intent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_AUDIO_ONLY)); /** * If there is an available {@link android.telecom.CallRedirectionService}, use the * {@link CallRedirectionProcessor} to perform call redirection instead of using * broadcasting. */ callRedirectionWithService = callRedirectionProcessor .canMakeCallRedirectionWithService(); if (callRedirectionWithService) { callRedirectionProcessor.performCallRedirection(); } } if (sendNewOutgoingCallBroadcast) { UserHandle targetUser = mCall.getInitiatingUser(); Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser); broadcastIntent(intent, number, !callImmediately, targetUser); broadcastIntent(intent, number, !callImmediately && !callRedirectionWithService, targetUser); } return DisconnectCause.NOT_DISCONNECTED; } Loading
src/com/android/server/telecom/Timeouts.java +2 −2 Original line number Diff line number Diff line Loading @@ -187,7 +187,7 @@ public final class Timeouts { */ public static long getUserDefinedCallRedirectionTimeoutMillis(ContentResolver contentResolver) { return get(contentResolver, "user_defined_call_redirection_timeout", 3000L /* 3 seconds */); 5000L /* 5 seconds */); } /** Loading @@ -196,6 +196,6 @@ public final class Timeouts { * @param contentResolver The content resolved. */ public static long getCarrierCallRedirectionTimeoutMillis(ContentResolver contentResolver) { return get(contentResolver, "carrier_call_redirection_timeout", 3000L /* 3 seconds */); return get(contentResolver, "carrier_call_redirection_timeout", 5000L /* 5 seconds */); } }
src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java +99 −86 Original line number Diff line number Diff line Loading @@ -16,26 +16,22 @@ package com.android.server.telecom.callredirection; import android.Manifest; import android.app.AppOpsManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.UserHandle; import android.telecom.CallRedirectionService; import android.telecom.GatewayInfo; import android.telecom.Log; import android.telecom.Logging.Runnable; import android.telecom.PhoneAccountHandle; import android.telephony.CarrierConfigManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telecom.ICallRedirectionAdapter; import com.android.internal.telecom.ICallRedirectionService; Loading @@ -46,8 +42,6 @@ import com.android.server.telecom.PhoneAccountRegistrar; import com.android.server.telecom.TelecomSystem; import com.android.server.telecom.Timeouts; import java.util.List; /** * A single instance of call redirection processor that handles the call redirection with * user-defined {@link CallRedirectionService} and carrier {@link CallRedirectionService} for a Loading Loading @@ -98,10 +92,14 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { private void onServiceBound(ICallRedirectionService service) { mService = service; try { mService.placeCall(new CallRedirectionAdapter(), mHandle, mPhoneAccountHandle); mHandle = mCallRedirectionProcessorHelper.formatNumberForRedirection(mHandle); // Telecom does not perform user interactions for carrier call redirection. mService.placeCall(new CallRedirectionAdapter(), mHandle, mPhoneAccountHandle, mAllowInteractiveResponse && mServiceType.equals(SERVICE_TYPE_USER_DEFINED)); Log.addEvent(mCall, mServiceType.equals(SERVICE_TYPE_USER_DEFINED) ? LogUtils.Events.REDIRECTION_SENT_USER : LogUtils.Events.REDIRECTION_SENT_CARRIER); : LogUtils.Events.REDIRECTION_SENT_CARRIER, mComponentName); Log.d(this, "Requested placeCall with [handle]" + Log.pii(mHandle) + " [phoneAccountHandle]" + mPhoneAccountHandle); } catch (RemoteException e) { Loading Loading @@ -189,16 +187,20 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { } @Override public void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount) { public void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount, boolean confirmFirst) { Log.startSession("CRA.rC"); long token = Binder.clearCallingIdentity(); try { synchronized (mTelecomLock) { mHandle = handle; mPhoneAccountHandle = targetPhoneAccount; mUiAction = (confirmFirst && mServiceType.equals(SERVICE_TYPE_USER_DEFINED) && mAllowInteractiveResponse) ? UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM : mUiAction; Log.d(this, "Received redirectCall with [handle]" + Log.pii(mHandle) + " [phoneAccountHandle]" + mPhoneAccountHandle + " from " + mServiceType + " call" + " redirection service"); + mServiceType + " call redirection service"); finishCallRedirection(); } } finally { Loading @@ -212,14 +214,23 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { private final Context mContext; private final CallsManager mCallsManager; private final Call mCall; private final PhoneAccountRegistrar mPhoneAccountRegistrar; private final boolean mAllowInteractiveResponse; private final GatewayInfo mGatewayInfo; private final boolean mSpeakerphoneOn; private final int mVideoState; private final Timeouts.Adapter mTimeoutsAdapter; private final TelecomSystem.SyncRoot mTelecomLock; private final Handler mHandler = new Handler(Looper.getMainLooper()); private CallRedirectionAttempt mAttempt; private CallRedirectionProcessorHelper mCallRedirectionProcessorHelper; public static final String SERVICE_TYPE_CARRIER = "carrier"; public static final String SERVICE_TYPE_USER_DEFINED = "user_defined"; public static final String UI_TYPE_NO_ACTION = "no_action"; public static final String UI_TYPE_USER_DEFINED_TIMEOUT = "user_defined_timeout"; public static final String UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM = "user_defined_ask_for_confirm"; private PhoneAccountHandle mPhoneAccountHandle; private Uri mHandle; Loading @@ -228,6 +239,10 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { * Indicates if Telecom should cancel the call when the whole call redirection finishes. */ private boolean mShouldCancelCall = false; /** * Indicates Telecom should handle different types of UI if need. */ private String mUiAction = UI_TYPE_NO_ACTION; /** * Indicates if Telecom is waiting for a callback from a user-defined * {@link CallRedirectionService}. Loading @@ -243,19 +258,28 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { Context context, CallsManager callsManager, Call call, PhoneAccountRegistrar phoneAccountRegistrar, Uri handle, PhoneAccountHandle phoneAccountHandle, Timeouts.Adapter timeoutsAdapter, TelecomSystem.SyncRoot lock) { PhoneAccountRegistrar phoneAccountRegistrar, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState) { mContext = context; mCallsManager = callsManager; mCall = call; mPhoneAccountRegistrar = phoneAccountRegistrar; mHandle = handle; mPhoneAccountHandle = phoneAccountHandle; mTimeoutsAdapter = timeoutsAdapter; mTelecomLock = lock; mPhoneAccountHandle = call.getTargetPhoneAccount(); mGatewayInfo = gatewayInfo; mSpeakerphoneOn = speakerphoneOn; mVideoState = videoState; mTimeoutsAdapter = callsManager.getTimeoutsAdapter(); mTelecomLock = callsManager.getLock(); /** * The current rule to decide whether the implemented {@link CallRedirectionService} should * allow interactive responses with users is only based on whether it is in car mode. */ mAllowInteractiveResponse = !callsManager.getSystemStateHelper().isCarMode(); mCallRedirectionProcessorHelper = new CallRedirectionProcessorHelper( context, callsManager, phoneAccountRegistrar); } @Override Loading @@ -264,11 +288,15 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { mHandler.post(new Runnable("CRP.oCRC", mTelecomLock) { @Override public void loggedRun() { mHandle = mCallRedirectionProcessorHelper.processNumberWhenRedirectionComplete( mHandle); if (mIsUserDefinedRedirectionPending) { Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_USER); mIsUserDefinedRedirectionPending = false; if (mShouldCancelCall) { // TODO mCallsManager.onCallRedirectionComplete mCallsManager.onCallRedirectionComplete(mCall, mHandle, mPhoneAccountHandle, mGatewayInfo, mSpeakerphoneOn, mVideoState, mShouldCancelCall, mUiAction); } else { performCarrierCallRedirection(); } Loading @@ -276,23 +304,33 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { if (mIsCarrierRedirectionPending) { Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_CARRIER); mIsCarrierRedirectionPending = false; // TODO mCallsManager.onCallRedirectionComplete mCallsManager.onCallRedirectionComplete(mCall, mHandle, mPhoneAccountHandle, mGatewayInfo, mSpeakerphoneOn, mVideoState, mShouldCancelCall, mUiAction); } } }.prepare()); } /* /** * The entry to perform call redirection of the call from (@link CallsManager) */ public void performCallRedirection() { // If the Gateway Info is set with intent, do not perform call redirection. if (mGatewayInfo != null) { mCallsManager.onCallRedirectionComplete(mCall, mHandle, mPhoneAccountHandle, mGatewayInfo, mSpeakerphoneOn, mVideoState, mShouldCancelCall, mUiAction); } else { mCallRedirectionProcessorHelper.storePostDialDigits(mHandle); performUserDefinedCallRedirection(); } } private void performUserDefinedCallRedirection() { Log.d(this, "performUserDefinedCallRedirection"); ComponentName componentName = getUserDefinedCallRedirectionService(mContext); if (componentName != null && canBindToCallRedirectionService(mContext, componentName)) { ComponentName componentName = mCallRedirectionProcessorHelper.getUserDefinedCallRedirectionService(); if (componentName != null) { mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_USER_DEFINED); mAttempt.process(); mIsUserDefinedRedirectionPending = true; Loading @@ -306,9 +344,10 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { private void performCarrierCallRedirection() { Log.d(this, "performCarrierCallRedirection"); ComponentName componentName = getCarrierCallRedirectionService( mContext, mPhoneAccountHandle); if (componentName != null && canBindToCallRedirectionService(mContext, componentName)) { ComponentName componentName = mCallRedirectionProcessorHelper.getCarrierCallRedirectionService( mPhoneAccountHandle); if (componentName != null) { mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_CARRIER); mAttempt.process(); mIsCarrierRedirectionPending = true; Loading @@ -316,7 +355,9 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { } else { Log.i(this, "There are no carrier call redirection services installed on this" + " device."); // TODO return to CallsManager.onCallRedirectionComplete mCallsManager.onCallRedirectionComplete(mCall, mHandle, mPhoneAccountHandle, mGatewayInfo, mSpeakerphoneOn, mVideoState, mShouldCancelCall, mUiAction); } } Loading @@ -333,77 +374,49 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { serviceType.equals(SERVICE_TYPE_USER_DEFINED) ? mIsUserDefinedRedirectionPending : mIsCarrierRedirectionPending; if (isCurrentRedirectionPending) { Log.i(CallRedirectionProcessor.this, serviceType + "call redirection has timed out."); Log.i(this, serviceType + " call redirection has timed out."); Log.addEvent(mCall, serviceType.equals(SERVICE_TYPE_USER_DEFINED) ? LogUtils.Events.REDIRECTION_TIMED_OUT_USER : LogUtils.Events.REDIRECTION_TIMED_OUT_CARRIER); if (serviceType.equals(SERVICE_TYPE_USER_DEFINED)) { mUiAction = UI_TYPE_USER_DEFINED_TIMEOUT; mShouldCancelCall = true; } onCallRedirectionComplete(mCall); } } }.prepare(), timeout); } private ComponentName getUserDefinedCallRedirectionService(Context context) { // TODO get service component name from settings default value: // android.provider.Settings#CALL_REDIRECTION_DEFAULT_APPLICATION return null; } private ComponentName getCarrierCallRedirectionService(Context context, PhoneAccountHandle targetPhoneAccountHandle) { CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); if (configManager == null) { Log.i(this, "Cannot get CarrierConfigManager."); return null; } PersistableBundle pb = configManager.getConfigForSubId(mPhoneAccountRegistrar .getSubscriptionIdForPhoneAccount(targetPhoneAccountHandle)); if (pb == null) { Log.i(this, "Cannot get PersistableBundle."); return null; } String componentNameString = pb.getString( CarrierConfigManager.KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING); return new ComponentName(context, componentNameString); } private boolean canBindToCallRedirectionService(Context context, ComponentName componentName) { Intent intent = new Intent(CallRedirectionService.SERVICE_INTERFACE); intent.setComponent(componentName); List<ResolveInfo> entries = mContext.getPackageManager().queryIntentServicesAsUser( intent, 0, mCallsManager.getCurrentUserHandle().getIdentifier()); if (entries.isEmpty()) { Log.i(this, "There are no call redirection services installed on this device."); return false; } else if (entries.size() != 1) { Log.i(this, "There are multiple call redirection services installed on this device."); return false; } else { ResolveInfo entry = entries.get(0); if (entry.serviceInfo.permission == null || !entry.serviceInfo.permission.equals( Manifest.permission.BIND_CALL_REDIRECTION_SERVICE)) { Log.w(this, "CallRedirectionService must require BIND_CALL_REDIRECTION_SERVICE" + " permission: " + entry.serviceInfo.packageName); return false; } AppOpsManager appOps = (AppOpsManager) context.getSystemService( Context.APP_OPS_SERVICE); if (appOps.noteOp(AppOpsManager.OP_PROCESS_OUTGOING_CALLS, Binder.getCallingUid(), entry.serviceInfo.packageName) != AppOpsManager.MODE_ALLOWED) { Log.w(this, "App Ops does not allow " + entry.serviceInfo.packageName); return false; } } return true; /** * Checks if Telecom can make call redirection with any available call redirection service. * * @return {@code true} if it can; {@code false} otherwise. */ public boolean canMakeCallRedirectionWithService() { boolean canMakeCallRedirectionWithService = mCallRedirectionProcessorHelper.getUserDefinedCallRedirectionService() != null || mCallRedirectionProcessorHelper.getCarrierCallRedirectionService( mPhoneAccountHandle) != null; Log.w(this, "Can make call redirection with any available service: " + canMakeCallRedirectionWithService); return canMakeCallRedirectionWithService; } /** * Returns the handler for testing purposes. * Returns the handler, for testing purposes. */ @VisibleForTesting public Handler getHandler() { return mHandler; } /** * Set CallRedirectionProcessorHelper for testing purposes. */ @VisibleForTesting public void setCallRedirectionServiceHelper( CallRedirectionProcessorHelper callRedirectionProcessorHelper) { mCallRedirectionProcessorHelper = callRedirectionProcessorHelper; } }
src/com/android/server/telecom/callredirection/CallRedirectionProcessorHelper.java 0 → 100644 +199 −0 File added.Preview size limit exceeded, changes collapsed. Show changes