Loading src/com/android/server/telecom/CallIntentProcessor.java +7 −4 Original line number Diff line number Diff line Loading @@ -178,12 +178,15 @@ public class CallIntentProcessor { NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster( context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(), isPrivilegedDialer); final int result = broadcaster.processIntent(); final boolean success = result == DisconnectCause.NOT_DISCONNECTED; if (!success && call != null) { disconnectCallAndShowErrorDialog(context, call, result); // If the broadcaster comes back with an immediate error, disconnect and show a dialog. NewOutgoingCallIntentBroadcaster.CallDisposition disposition = broadcaster.evaluateCall(); if (disposition.disconnectCause != DisconnectCause.NOT_DISCONNECTED) { disconnectCallAndShowErrorDialog(context, call, disposition.disconnectCause); return; } broadcaster.processCall(disposition); } /** Loading src/com/android/server/telecom/CallsManager.java +7 −29 Original line number Diff line number Diff line Loading @@ -102,7 +102,6 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.IntStream; Loading Loading @@ -150,27 +149,6 @@ public class CallsManager extends Call.ListenerBase void performAction(); } /** An executor that starts a log session before executing a runnable */ private class LoggedHandlerExecutor implements Executor { private Handler mHandler; private String mSessionName; public LoggedHandlerExecutor(Handler handler, String sessionName) { mHandler = handler; mSessionName = sessionName; } @Override public void execute(java.lang.Runnable command) { mHandler.post(new Runnable(mSessionName, mLock) { @Override public void loggedRun() { command.run(); } }.prepare()); } } private static final String TAG = "CallsManager"; /** Loading Loading @@ -1319,7 +1297,7 @@ public class CallsManager extends Call.ListenerBase CompletableFuture.completedFuture((Void) null).thenComposeAsync((x) -> findOutgoingCallPhoneAccount(requestedAccountHandle, handle, VideoProfile.isVideo(finalVideoState), initiatingUser), new LoggedHandlerExecutor(outgoingCallHandler, "CM.fOCP")); new LoggedHandlerExecutor(outgoingCallHandler, "CM.fOCP", mLock)); // This is a block of code that executes after the list of potential phone accts has been // retrieved. Loading @@ -1333,7 +1311,7 @@ public class CallsManager extends Call.ListenerBase phoneAccountHandle = null; } finalCall.setTargetPhoneAccount(phoneAccountHandle); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.sOCPA")); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.sOCPA", mLock)); // This composes the future containing the potential phone accounts with code that queries Loading @@ -1352,7 +1330,7 @@ public class CallsManager extends Call.ListenerBase } return PhoneAccountSuggestionHelper.bindAndGetSuggestions(mContext, finalCall.getHandle(), potentialPhoneAccounts); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.cOCSS")); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.cOCSS", mLock)); // This future checks the status of existing calls and attempts to make room for the Loading Loading @@ -1393,7 +1371,7 @@ public class CallsManager extends Call.ListenerBase return CompletableFuture.completedFuture(null); } return CompletableFuture.completedFuture(finalCall); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSMCP")); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSMCP", mLock)); // The outgoing call can be placed, go forward. This future glues together the results of // the account suggestion stage and the make room for call stage. Loading Loading @@ -1447,7 +1425,7 @@ public class CallsManager extends Call.ListenerBase addCall(callToPlace); return mPendingAccountSelection; }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSPA")); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSPA", mLock)); // Potentially perform call identification for dialed TEL scheme numbers. if (PhoneAccount.SCHEME_TEL.equals(handle.getScheme())) { Loading @@ -1474,7 +1452,7 @@ public class CallsManager extends Call.ListenerBase if (!isInContacts) { performCallIdentification(theCall); } }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pCSB")); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pCSB", mLock)); } // Finally, after all user interaction is complete, we execute this code to finish setting Loading Loading @@ -1534,7 +1512,7 @@ public class CallsManager extends Call.ListenerBase addCall(callToUse); } return CompletableFuture.completedFuture(callToUse); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pASP")); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pASP", mLock)); return mLatestPostSelectionProcessingFuture; } Loading src/com/android/server/telecom/LoggedHandlerExecutor.java 0 → 100644 +45 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.telecom; import android.os.Handler; import android.telecom.Logging.Runnable; import java.util.concurrent.Executor; /** An executor that starts a log session before executing a runnable */ public class LoggedHandlerExecutor implements Executor { private Handler mHandler; private String mSessionName; private TelecomSystem.SyncRoot mLock; public LoggedHandlerExecutor(Handler handler, String sessionName, TelecomSystem.SyncRoot lock) { mHandler = handler; mSessionName = sessionName; mLock = lock; } @Override public void execute(java.lang.Runnable command) { mHandler.post(new Runnable(mSessionName, mLock) { @Override public void loggedRun() { command.run(); } }.prepare()); } } src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java +108 −83 Original line number Diff line number Diff line Loading @@ -82,6 +82,19 @@ public class NewOutgoingCallIntentBroadcaster { */ private final boolean mIsDefaultOrSystemPhoneApp; public static class CallDisposition { // True for certain types of numbers that are not intended to be intercepted or modified // by third parties (e.g. emergency numbers). public boolean callImmediately = false; // True for all managed calls, false for self-managed calls. public boolean sendBroadcast = true; // True for requesting call redirection, false for not requesting it. public boolean requestRedirection = true; public int disconnectCause = DisconnectCause.NOT_DISCONNECTED; String number; Uri callingAddress; } @VisibleForTesting public NewOutgoingCallIntentBroadcaster(Context context, CallsManager callsManager, Call call, Intent intent, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, Loading Loading @@ -193,8 +206,8 @@ public class NewOutgoingCallIntentBroadcaster { * {@link DisconnectCause} if the call did not, describing why it failed. */ @VisibleForTesting public int processIntent() { Log.v(this, "Processing call intent in OutgoingCallIntentBroadcaster."); public CallDisposition evaluateCall() { CallDisposition result = new CallDisposition(); Intent intent = mIntent; String action = intent.getAction(); Loading @@ -202,7 +215,8 @@ public class NewOutgoingCallIntentBroadcaster { if (handle == null) { Log.w(this, "Empty handle obtained from the call intent."); return DisconnectCause.INVALID_NUMBER; result.disconnectCause = DisconnectCause.INVALID_NUMBER; return result; } boolean isVoicemailNumber = PhoneAccount.SCHEME_VOICEMAIL.equals(handle.getScheme()); Loading @@ -210,16 +224,18 @@ public class NewOutgoingCallIntentBroadcaster { if (Intent.ACTION_CALL.equals(action) || Intent.ACTION_CALL_PRIVILEGED.equals(action)) { // Voicemail calls will be handled directly by the telephony connection manager boolean speakerphoneOn = mIntent.getBooleanExtra( TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false); placeOutgoingCallImmediately(mCall, handle, null, speakerphoneOn, Log.i(this, "Voicemail number dialed. Skipping redirection and broadcast", intent); mIntent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_AUDIO_ONLY); return DisconnectCause.NOT_DISCONNECTED; result.callImmediately = true; result.requestRedirection = false; result.sendBroadcast = false; result.callingAddress = handle; return result; } else { Log.i(this, "Unhandled intent %s. Ignoring and not placing call.", intent); return DisconnectCause.OUTGOING_CANCELED; result.disconnectCause = DisconnectCause.OUTGOING_CANCELED; return result; } } Loading @@ -235,37 +251,31 @@ public class NewOutgoingCallIntentBroadcaster { } } String number = ""; // True for certain types of numbers that are not intended to be intercepted or modified // by third parties (e.g. emergency numbers). 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; result.number = ""; result.callingAddress = handle; if (!isSelfManaged) { // Placing a managed call number = mPhoneNumberUtilsAdapter.getNumberFromIntent(intent, mContext); if (TextUtils.isEmpty(number)) { Log.w(this, "Empty number obtained from the call intent."); return DisconnectCause.NO_PHONE_NUMBER_SUPPLIED; if (isSelfManaged) { // Self-managed call. result.callImmediately = true; result.sendBroadcast = false; result.requestRedirection = false; Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call."); return result; } // TODO: Cleanup this dialing code; it makes the assumption that we're dialing with a // SIP or TEL URI. boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number); if (!isUriNumber) { number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number); number = mPhoneNumberUtilsAdapter.stripSeparators(number); // Placing a managed call String number = getNumberFromCallIntent(intent); result.number = number; if (number == null) { result.disconnectCause = DisconnectCause.NO_PHONE_NUMBER_SUPPLIED; return result; } final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number); Log.v(this, "isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber); rewriteCallIntentAction(intent, isPotentialEmergencyNumber); action = intent.getAction(); action = calculateCallIntentAction(intent, isPotentialEmergencyNumber); intent.setAction(action); if (Intent.ACTION_CALL.equals(action)) { if (isPotentialEmergencyNumber) { Loading @@ -273,41 +283,56 @@ public class NewOutgoingCallIntentBroadcaster { Log.w(this, "Cannot call potential emergency number %s with CALL Intent %s " + "unless caller is system or default dialer.", number, intent); launchSystemDialer(intent.getData()); return DisconnectCause.OUTGOING_CANCELED; result.disconnectCause = DisconnectCause.OUTGOING_CANCELED; return result; } else { callImmediately = true; result.callImmediately = true; } } } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) { if (!isPotentialEmergencyNumber) { Log.w(this, "Cannot call non-potential-emergency number %s with EMERGENCY_CALL " + "Intent %s.", number, intent); return DisconnectCause.OUTGOING_CANCELED; result.disconnectCause = DisconnectCause.OUTGOING_CANCELED; return result; } callImmediately = true; result.callImmediately = true; } else { Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent); return DisconnectCause.INVALID_NUMBER; result.disconnectCause = DisconnectCause.INVALID_NUMBER; return result; } // TODO: Support dialing using URIs instead of just assuming SIP or TEL. String scheme = isUriNumber ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL; callingAddress = Uri.fromParts(scheme, number, null); } else { // Self-managed call. callImmediately = true; sendNewOutgoingCallBroadcast = false; requestCallRedirection = false; Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call."); String scheme = mPhoneNumberUtilsAdapter.isUriNumber(number) ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL; result.callingAddress = Uri.fromParts(scheme, number, null); return result; } private String getNumberFromCallIntent(Intent intent) { String number; number = mPhoneNumberUtilsAdapter.getNumberFromIntent(intent, mContext); if (TextUtils.isEmpty(number)) { Log.w(this, "Empty number obtained from the call intent."); return null; } if (callImmediately) { boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number); if (!isUriNumber) { number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number); number = mPhoneNumberUtilsAdapter.stripSeparators(number); } return number; } public void processCall(CallDisposition disposition) { if (disposition.callImmediately) { boolean speakerphoneOn = mIntent.getBooleanExtra( TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false); int videoState = mIntent.getIntExtra( TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_AUDIO_ONLY); placeOutgoingCallImmediately(mCall, callingAddress, null, placeOutgoingCallImmediately(mCall, disposition.callingAddress, null, speakerphoneOn, videoState); // Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast Loading @@ -317,14 +342,14 @@ public class NewOutgoingCallIntentBroadcaster { } boolean callRedirectionWithService = false; if (requestCallRedirection) { if (disposition.requestRedirection) { CallRedirectionProcessor callRedirectionProcessor = new CallRedirectionProcessor( mContext, mCallsManager, mCall, callingAddress, mContext, mCallsManager, mCall, disposition.callingAddress, mCallsManager.getPhoneAccountRegistrar(), getGateWayInfoFromIntent(intent, handle), intent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, getGateWayInfoFromIntent(mIntent, mIntent.getData()), mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false), intent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_AUDIO_ONLY)); /** * If there is an available {@link android.telecom.CallRedirectionService}, use the Loading @@ -338,13 +363,12 @@ public class NewOutgoingCallIntentBroadcaster { } } if (sendNewOutgoingCallBroadcast) { if (disposition.sendBroadcast) { UserHandle targetUser = mCall.getInitiatingUser(); Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser); broadcastIntent(intent, number, !callImmediately && !callRedirectionWithService, targetUser); broadcastIntent(mIntent, disposition.number, !disposition.callImmediately && !callRedirectionWithService, targetUser); } return DisconnectCause.NOT_DISCONNECTED; } /** Loading Loading @@ -494,14 +518,15 @@ public class NewOutgoingCallIntentBroadcaster { } /** * Given a call intent and whether or not the number to dial is an emergency number, rewrite * the call intent action to an appropriate one. * Given a call intent and whether or not the number to dial is an emergency number, determine * the appropriate call intent action. * * @param intent Intent to rewrite the action for * @param intent Intent to evaluate * @param isPotentialEmergencyNumber Whether or not the number is potentially an emergency * number. * @return The appropriate action. */ private void rewriteCallIntentAction(Intent intent, boolean isPotentialEmergencyNumber) { private String calculateCallIntentAction(Intent intent, boolean isPotentialEmergencyNumber) { String action = intent.getAction(); /* Change CALL_PRIVILEGED into CALL or CALL_EMERGENCY as needed. */ Loading @@ -514,8 +539,8 @@ public class NewOutgoingCallIntentBroadcaster { action = Intent.ACTION_CALL; } Log.v(this, " - updating action from CALL_PRIVILEGED to %s", action); intent.setAction(action); } return action; } private long getDisconnectTimeoutFromApp(Bundle resultExtras, long defaultTimeout) { Loading tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java +14 −10 Original line number Diff line number Diff line Loading @@ -107,7 +107,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { @Test public void testNullHandle() { Intent intent = new Intent(Intent.ACTION_CALL, null); int result = processIntent(intent, true); int result = processIntent(intent, true).disconnectCause; assertEquals(DisconnectCause.INVALID_NUMBER, result); verifyNoBroadcastSent(); verifyNoCallPlaced(); Loading @@ -120,7 +120,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(voicemailNumber)); intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true); int result = processIntent(intent, true); int result = processIntent(intent, true).disconnectCause; assertEquals(DisconnectCause.NOT_DISCONNECTED, result); verify(mCallsManager).placeOutgoingCall(eq(mCall), eq(Uri.parse(voicemailNumber)), Loading Loading @@ -148,7 +148,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { private void badCallActionHelper(Uri handle, int expectedCode) { Intent intent = new Intent(Intent.ACTION_ALARM_CHANGED, handle); int result = processIntent(intent, true); int result = processIntent(intent, true).disconnectCause; assertEquals(expectedCode, result); verifyNoBroadcastSent(); Loading Loading @@ -176,7 +176,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { Uri handle = Uri.parse("tel:"); Intent intent = new Intent(Intent.ACTION_CALL, handle); int result = processIntent(intent, true); int result = processIntent(intent, true).disconnectCause; assertEquals(DisconnectCause.NO_PHONE_NUMBER_SUPPLIED, result); verifyNoBroadcastSent(); Loading @@ -197,7 +197,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { mComponentContextFixture.putResource(R.string.dialer_default_class, dialer_default_class_string); int result = processIntent(intent, false); int result = processIntent(intent, false).disconnectCause; assertEquals(DisconnectCause.OUTGOING_CANCELED, result); verifyNoBroadcastSent(); Loading Loading @@ -257,7 +257,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { doReturn(false).when(mPhoneNumberUtilsAdapterSpy).isPotentialLocalEmergencyNumber( any(Context.class), eq(handle.getSchemeSpecificPart())); Intent intent = new Intent(Intent.ACTION_CALL_EMERGENCY, handle); int result = processIntent(intent, true); int result = processIntent(intent, true).disconnectCause; assertEquals(DisconnectCause.OUTGOING_CANCELED, result); verifyNoCallPlaced(); Loading @@ -272,7 +272,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { any(Context.class), eq(handle.getSchemeSpecificPart())); intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, isSpeakerphoneOn); intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState); int result = processIntent(intent, true); int result = processIntent(intent, true).disconnectCause; assertEquals(DisconnectCause.NOT_DISCONNECTED, result); verify(mCallsManager).placeOutgoingCall(eq(mCall), eq(handle), isNull(GatewayInfo.class), Loading Loading @@ -401,7 +401,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, isSpeakerphoneOn); intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState); int result = processIntent(intent, true); int result = processIntent(intent, true).disconnectCause; assertEquals(DisconnectCause.NOT_DISCONNECTED, result); Bundle expectedExtras = createNumberExtras(handle.getSchemeSpecificPart()); Loading @@ -419,12 +419,16 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { return i; } private int processIntent(Intent intent, private NewOutgoingCallIntentBroadcaster.CallDisposition processIntent(Intent intent, boolean isDefaultPhoneApp) { NewOutgoingCallIntentBroadcaster b = new NewOutgoingCallIntentBroadcaster( mContext, mCallsManager, mCall, intent, mPhoneNumberUtilsAdapterSpy, isDefaultPhoneApp); return b.processIntent(); NewOutgoingCallIntentBroadcaster.CallDisposition cd = b.evaluateCall(); if (cd.disconnectCause == DisconnectCause.NOT_DISCONNECTED) { b.processCall(cd); } return cd; } private ReceiverIntentPair verifyBroadcastSent(String number, Bundle expectedExtras) { Loading Loading
src/com/android/server/telecom/CallIntentProcessor.java +7 −4 Original line number Diff line number Diff line Loading @@ -178,12 +178,15 @@ public class CallIntentProcessor { NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster( context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(), isPrivilegedDialer); final int result = broadcaster.processIntent(); final boolean success = result == DisconnectCause.NOT_DISCONNECTED; if (!success && call != null) { disconnectCallAndShowErrorDialog(context, call, result); // If the broadcaster comes back with an immediate error, disconnect and show a dialog. NewOutgoingCallIntentBroadcaster.CallDisposition disposition = broadcaster.evaluateCall(); if (disposition.disconnectCause != DisconnectCause.NOT_DISCONNECTED) { disconnectCallAndShowErrorDialog(context, call, disposition.disconnectCause); return; } broadcaster.processCall(disposition); } /** Loading
src/com/android/server/telecom/CallsManager.java +7 −29 Original line number Diff line number Diff line Loading @@ -102,7 +102,6 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.IntStream; Loading Loading @@ -150,27 +149,6 @@ public class CallsManager extends Call.ListenerBase void performAction(); } /** An executor that starts a log session before executing a runnable */ private class LoggedHandlerExecutor implements Executor { private Handler mHandler; private String mSessionName; public LoggedHandlerExecutor(Handler handler, String sessionName) { mHandler = handler; mSessionName = sessionName; } @Override public void execute(java.lang.Runnable command) { mHandler.post(new Runnable(mSessionName, mLock) { @Override public void loggedRun() { command.run(); } }.prepare()); } } private static final String TAG = "CallsManager"; /** Loading Loading @@ -1319,7 +1297,7 @@ public class CallsManager extends Call.ListenerBase CompletableFuture.completedFuture((Void) null).thenComposeAsync((x) -> findOutgoingCallPhoneAccount(requestedAccountHandle, handle, VideoProfile.isVideo(finalVideoState), initiatingUser), new LoggedHandlerExecutor(outgoingCallHandler, "CM.fOCP")); new LoggedHandlerExecutor(outgoingCallHandler, "CM.fOCP", mLock)); // This is a block of code that executes after the list of potential phone accts has been // retrieved. Loading @@ -1333,7 +1311,7 @@ public class CallsManager extends Call.ListenerBase phoneAccountHandle = null; } finalCall.setTargetPhoneAccount(phoneAccountHandle); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.sOCPA")); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.sOCPA", mLock)); // This composes the future containing the potential phone accounts with code that queries Loading @@ -1352,7 +1330,7 @@ public class CallsManager extends Call.ListenerBase } return PhoneAccountSuggestionHelper.bindAndGetSuggestions(mContext, finalCall.getHandle(), potentialPhoneAccounts); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.cOCSS")); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.cOCSS", mLock)); // This future checks the status of existing calls and attempts to make room for the Loading Loading @@ -1393,7 +1371,7 @@ public class CallsManager extends Call.ListenerBase return CompletableFuture.completedFuture(null); } return CompletableFuture.completedFuture(finalCall); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSMCP")); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSMCP", mLock)); // The outgoing call can be placed, go forward. This future glues together the results of // the account suggestion stage and the make room for call stage. Loading Loading @@ -1447,7 +1425,7 @@ public class CallsManager extends Call.ListenerBase addCall(callToPlace); return mPendingAccountSelection; }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSPA")); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSPA", mLock)); // Potentially perform call identification for dialed TEL scheme numbers. if (PhoneAccount.SCHEME_TEL.equals(handle.getScheme())) { Loading @@ -1474,7 +1452,7 @@ public class CallsManager extends Call.ListenerBase if (!isInContacts) { performCallIdentification(theCall); } }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pCSB")); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pCSB", mLock)); } // Finally, after all user interaction is complete, we execute this code to finish setting Loading Loading @@ -1534,7 +1512,7 @@ public class CallsManager extends Call.ListenerBase addCall(callToUse); } return CompletableFuture.completedFuture(callToUse); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pASP")); }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pASP", mLock)); return mLatestPostSelectionProcessingFuture; } Loading
src/com/android/server/telecom/LoggedHandlerExecutor.java 0 → 100644 +45 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.telecom; import android.os.Handler; import android.telecom.Logging.Runnable; import java.util.concurrent.Executor; /** An executor that starts a log session before executing a runnable */ public class LoggedHandlerExecutor implements Executor { private Handler mHandler; private String mSessionName; private TelecomSystem.SyncRoot mLock; public LoggedHandlerExecutor(Handler handler, String sessionName, TelecomSystem.SyncRoot lock) { mHandler = handler; mSessionName = sessionName; mLock = lock; } @Override public void execute(java.lang.Runnable command) { mHandler.post(new Runnable(mSessionName, mLock) { @Override public void loggedRun() { command.run(); } }.prepare()); } }
src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java +108 −83 Original line number Diff line number Diff line Loading @@ -82,6 +82,19 @@ public class NewOutgoingCallIntentBroadcaster { */ private final boolean mIsDefaultOrSystemPhoneApp; public static class CallDisposition { // True for certain types of numbers that are not intended to be intercepted or modified // by third parties (e.g. emergency numbers). public boolean callImmediately = false; // True for all managed calls, false for self-managed calls. public boolean sendBroadcast = true; // True for requesting call redirection, false for not requesting it. public boolean requestRedirection = true; public int disconnectCause = DisconnectCause.NOT_DISCONNECTED; String number; Uri callingAddress; } @VisibleForTesting public NewOutgoingCallIntentBroadcaster(Context context, CallsManager callsManager, Call call, Intent intent, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, Loading Loading @@ -193,8 +206,8 @@ public class NewOutgoingCallIntentBroadcaster { * {@link DisconnectCause} if the call did not, describing why it failed. */ @VisibleForTesting public int processIntent() { Log.v(this, "Processing call intent in OutgoingCallIntentBroadcaster."); public CallDisposition evaluateCall() { CallDisposition result = new CallDisposition(); Intent intent = mIntent; String action = intent.getAction(); Loading @@ -202,7 +215,8 @@ public class NewOutgoingCallIntentBroadcaster { if (handle == null) { Log.w(this, "Empty handle obtained from the call intent."); return DisconnectCause.INVALID_NUMBER; result.disconnectCause = DisconnectCause.INVALID_NUMBER; return result; } boolean isVoicemailNumber = PhoneAccount.SCHEME_VOICEMAIL.equals(handle.getScheme()); Loading @@ -210,16 +224,18 @@ public class NewOutgoingCallIntentBroadcaster { if (Intent.ACTION_CALL.equals(action) || Intent.ACTION_CALL_PRIVILEGED.equals(action)) { // Voicemail calls will be handled directly by the telephony connection manager boolean speakerphoneOn = mIntent.getBooleanExtra( TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false); placeOutgoingCallImmediately(mCall, handle, null, speakerphoneOn, Log.i(this, "Voicemail number dialed. Skipping redirection and broadcast", intent); mIntent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_AUDIO_ONLY); return DisconnectCause.NOT_DISCONNECTED; result.callImmediately = true; result.requestRedirection = false; result.sendBroadcast = false; result.callingAddress = handle; return result; } else { Log.i(this, "Unhandled intent %s. Ignoring and not placing call.", intent); return DisconnectCause.OUTGOING_CANCELED; result.disconnectCause = DisconnectCause.OUTGOING_CANCELED; return result; } } Loading @@ -235,37 +251,31 @@ public class NewOutgoingCallIntentBroadcaster { } } String number = ""; // True for certain types of numbers that are not intended to be intercepted or modified // by third parties (e.g. emergency numbers). 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; result.number = ""; result.callingAddress = handle; if (!isSelfManaged) { // Placing a managed call number = mPhoneNumberUtilsAdapter.getNumberFromIntent(intent, mContext); if (TextUtils.isEmpty(number)) { Log.w(this, "Empty number obtained from the call intent."); return DisconnectCause.NO_PHONE_NUMBER_SUPPLIED; if (isSelfManaged) { // Self-managed call. result.callImmediately = true; result.sendBroadcast = false; result.requestRedirection = false; Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call."); return result; } // TODO: Cleanup this dialing code; it makes the assumption that we're dialing with a // SIP or TEL URI. boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number); if (!isUriNumber) { number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number); number = mPhoneNumberUtilsAdapter.stripSeparators(number); // Placing a managed call String number = getNumberFromCallIntent(intent); result.number = number; if (number == null) { result.disconnectCause = DisconnectCause.NO_PHONE_NUMBER_SUPPLIED; return result; } final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number); Log.v(this, "isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber); rewriteCallIntentAction(intent, isPotentialEmergencyNumber); action = intent.getAction(); action = calculateCallIntentAction(intent, isPotentialEmergencyNumber); intent.setAction(action); if (Intent.ACTION_CALL.equals(action)) { if (isPotentialEmergencyNumber) { Loading @@ -273,41 +283,56 @@ public class NewOutgoingCallIntentBroadcaster { Log.w(this, "Cannot call potential emergency number %s with CALL Intent %s " + "unless caller is system or default dialer.", number, intent); launchSystemDialer(intent.getData()); return DisconnectCause.OUTGOING_CANCELED; result.disconnectCause = DisconnectCause.OUTGOING_CANCELED; return result; } else { callImmediately = true; result.callImmediately = true; } } } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) { if (!isPotentialEmergencyNumber) { Log.w(this, "Cannot call non-potential-emergency number %s with EMERGENCY_CALL " + "Intent %s.", number, intent); return DisconnectCause.OUTGOING_CANCELED; result.disconnectCause = DisconnectCause.OUTGOING_CANCELED; return result; } callImmediately = true; result.callImmediately = true; } else { Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent); return DisconnectCause.INVALID_NUMBER; result.disconnectCause = DisconnectCause.INVALID_NUMBER; return result; } // TODO: Support dialing using URIs instead of just assuming SIP or TEL. String scheme = isUriNumber ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL; callingAddress = Uri.fromParts(scheme, number, null); } else { // Self-managed call. callImmediately = true; sendNewOutgoingCallBroadcast = false; requestCallRedirection = false; Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call."); String scheme = mPhoneNumberUtilsAdapter.isUriNumber(number) ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL; result.callingAddress = Uri.fromParts(scheme, number, null); return result; } private String getNumberFromCallIntent(Intent intent) { String number; number = mPhoneNumberUtilsAdapter.getNumberFromIntent(intent, mContext); if (TextUtils.isEmpty(number)) { Log.w(this, "Empty number obtained from the call intent."); return null; } if (callImmediately) { boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number); if (!isUriNumber) { number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number); number = mPhoneNumberUtilsAdapter.stripSeparators(number); } return number; } public void processCall(CallDisposition disposition) { if (disposition.callImmediately) { boolean speakerphoneOn = mIntent.getBooleanExtra( TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false); int videoState = mIntent.getIntExtra( TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_AUDIO_ONLY); placeOutgoingCallImmediately(mCall, callingAddress, null, placeOutgoingCallImmediately(mCall, disposition.callingAddress, null, speakerphoneOn, videoState); // Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast Loading @@ -317,14 +342,14 @@ public class NewOutgoingCallIntentBroadcaster { } boolean callRedirectionWithService = false; if (requestCallRedirection) { if (disposition.requestRedirection) { CallRedirectionProcessor callRedirectionProcessor = new CallRedirectionProcessor( mContext, mCallsManager, mCall, callingAddress, mContext, mCallsManager, mCall, disposition.callingAddress, mCallsManager.getPhoneAccountRegistrar(), getGateWayInfoFromIntent(intent, handle), intent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, getGateWayInfoFromIntent(mIntent, mIntent.getData()), mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false), intent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_AUDIO_ONLY)); /** * If there is an available {@link android.telecom.CallRedirectionService}, use the Loading @@ -338,13 +363,12 @@ public class NewOutgoingCallIntentBroadcaster { } } if (sendNewOutgoingCallBroadcast) { if (disposition.sendBroadcast) { UserHandle targetUser = mCall.getInitiatingUser(); Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser); broadcastIntent(intent, number, !callImmediately && !callRedirectionWithService, targetUser); broadcastIntent(mIntent, disposition.number, !disposition.callImmediately && !callRedirectionWithService, targetUser); } return DisconnectCause.NOT_DISCONNECTED; } /** Loading Loading @@ -494,14 +518,15 @@ public class NewOutgoingCallIntentBroadcaster { } /** * Given a call intent and whether or not the number to dial is an emergency number, rewrite * the call intent action to an appropriate one. * Given a call intent and whether or not the number to dial is an emergency number, determine * the appropriate call intent action. * * @param intent Intent to rewrite the action for * @param intent Intent to evaluate * @param isPotentialEmergencyNumber Whether or not the number is potentially an emergency * number. * @return The appropriate action. */ private void rewriteCallIntentAction(Intent intent, boolean isPotentialEmergencyNumber) { private String calculateCallIntentAction(Intent intent, boolean isPotentialEmergencyNumber) { String action = intent.getAction(); /* Change CALL_PRIVILEGED into CALL or CALL_EMERGENCY as needed. */ Loading @@ -514,8 +539,8 @@ public class NewOutgoingCallIntentBroadcaster { action = Intent.ACTION_CALL; } Log.v(this, " - updating action from CALL_PRIVILEGED to %s", action); intent.setAction(action); } return action; } private long getDisconnectTimeoutFromApp(Bundle resultExtras, long defaultTimeout) { Loading
tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java +14 −10 Original line number Diff line number Diff line Loading @@ -107,7 +107,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { @Test public void testNullHandle() { Intent intent = new Intent(Intent.ACTION_CALL, null); int result = processIntent(intent, true); int result = processIntent(intent, true).disconnectCause; assertEquals(DisconnectCause.INVALID_NUMBER, result); verifyNoBroadcastSent(); verifyNoCallPlaced(); Loading @@ -120,7 +120,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(voicemailNumber)); intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true); int result = processIntent(intent, true); int result = processIntent(intent, true).disconnectCause; assertEquals(DisconnectCause.NOT_DISCONNECTED, result); verify(mCallsManager).placeOutgoingCall(eq(mCall), eq(Uri.parse(voicemailNumber)), Loading Loading @@ -148,7 +148,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { private void badCallActionHelper(Uri handle, int expectedCode) { Intent intent = new Intent(Intent.ACTION_ALARM_CHANGED, handle); int result = processIntent(intent, true); int result = processIntent(intent, true).disconnectCause; assertEquals(expectedCode, result); verifyNoBroadcastSent(); Loading Loading @@ -176,7 +176,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { Uri handle = Uri.parse("tel:"); Intent intent = new Intent(Intent.ACTION_CALL, handle); int result = processIntent(intent, true); int result = processIntent(intent, true).disconnectCause; assertEquals(DisconnectCause.NO_PHONE_NUMBER_SUPPLIED, result); verifyNoBroadcastSent(); Loading @@ -197,7 +197,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { mComponentContextFixture.putResource(R.string.dialer_default_class, dialer_default_class_string); int result = processIntent(intent, false); int result = processIntent(intent, false).disconnectCause; assertEquals(DisconnectCause.OUTGOING_CANCELED, result); verifyNoBroadcastSent(); Loading Loading @@ -257,7 +257,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { doReturn(false).when(mPhoneNumberUtilsAdapterSpy).isPotentialLocalEmergencyNumber( any(Context.class), eq(handle.getSchemeSpecificPart())); Intent intent = new Intent(Intent.ACTION_CALL_EMERGENCY, handle); int result = processIntent(intent, true); int result = processIntent(intent, true).disconnectCause; assertEquals(DisconnectCause.OUTGOING_CANCELED, result); verifyNoCallPlaced(); Loading @@ -272,7 +272,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { any(Context.class), eq(handle.getSchemeSpecificPart())); intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, isSpeakerphoneOn); intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState); int result = processIntent(intent, true); int result = processIntent(intent, true).disconnectCause; assertEquals(DisconnectCause.NOT_DISCONNECTED, result); verify(mCallsManager).placeOutgoingCall(eq(mCall), eq(handle), isNull(GatewayInfo.class), Loading Loading @@ -401,7 +401,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, isSpeakerphoneOn); intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState); int result = processIntent(intent, true); int result = processIntent(intent, true).disconnectCause; assertEquals(DisconnectCause.NOT_DISCONNECTED, result); Bundle expectedExtras = createNumberExtras(handle.getSchemeSpecificPart()); Loading @@ -419,12 +419,16 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { return i; } private int processIntent(Intent intent, private NewOutgoingCallIntentBroadcaster.CallDisposition processIntent(Intent intent, boolean isDefaultPhoneApp) { NewOutgoingCallIntentBroadcaster b = new NewOutgoingCallIntentBroadcaster( mContext, mCallsManager, mCall, intent, mPhoneNumberUtilsAdapterSpy, isDefaultPhoneApp); return b.processIntent(); NewOutgoingCallIntentBroadcaster.CallDisposition cd = b.evaluateCall(); if (cd.disconnectCause == DisconnectCause.NOT_DISCONNECTED) { b.processCall(cd); } return cd; } private ReceiverIntentPair verifyBroadcastSent(String number, Bundle expectedExtras) { Loading