Merge "Add script for semi-automating Telecom testing"
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 8555c20..c2d4aa5 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -1148,7 +1148,8 @@
/**
* Attempts to disconnect the call through the connection service.
*/
- void disconnect(boolean wasViaNewOutgoingCallBroadcaster) {
+ @VisibleForTesting
+ public void disconnect(boolean wasViaNewOutgoingCallBroadcaster) {
Log.event(this, Log.Events.REQUEST_DISCONNECT);
// Track that the call is now locally disconnecting.
diff --git a/src/com/android/server/telecom/CallIntentProcessor.java b/src/com/android/server/telecom/CallIntentProcessor.java
index 831b708..9150084 100644
--- a/src/com/android/server/telecom/CallIntentProcessor.java
+++ b/src/com/android/server/telecom/CallIntentProcessor.java
@@ -139,7 +139,8 @@
// process will be running throughout the duration of the phone call and should never
// be killed.
NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
- context, callsManager, call, intent, isPrivilegedDialer);
+ context, callsManager, call, intent, new PhoneNumberUtilsAdapterImpl(),
+ isPrivilegedDialer);
final int result = broadcaster.processIntent();
final boolean success = result == DisconnectCause.NOT_DISCONNECTED;
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index f388977..c885181 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -782,8 +782,9 @@
* @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects.
* @param videoState The desired video state for the outgoing call.
*/
- void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn,
- int videoState) {
+ @VisibleForTesting
+ public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,
+ boolean speakerphoneOn, int videoState) {
if (call == null) {
// don't do anything if the call no longer exists
Log.i(this, "Canceling unknown call.");
diff --git a/src/com/android/server/telecom/Log.java b/src/com/android/server/telecom/Log.java
index 075f4bb..06a6243 100644
--- a/src/com/android/server/telecom/Log.java
+++ b/src/com/android/server/telecom/Log.java
@@ -305,6 +305,8 @@
}
}
+ public static final long DEFAULT_SESSION_TIMEOUT_MS = 30000L; // 30 seconds
+
@VisibleForTesting
public static void setTag(String tag) {
TAG = tag;
@@ -319,12 +321,16 @@
public interface ISessionCleanupTimeoutMs {
long get();
}
-
@VisibleForTesting
public static ISessionCleanupTimeoutMs sSessionCleanupTimeoutMs =
new ISessionCleanupTimeoutMs() {
@Override
public long get() {
+ // mContext will be null if Log is called from another process
+ // (UserCallActivity, for example). For these cases, use the default value.
+ if(mContext == null) {
+ return DEFAULT_SESSION_TIMEOUT_MS;
+ }
return Timeouts.getStaleSessionCleanupTimeoutMillis(
mContext.getContentResolver());
}
diff --git a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
index d9c6c33..a842911 100644
--- a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
+++ b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
@@ -31,9 +31,10 @@
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.telephony.DisconnectCause;
-import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
+import com.android.internal.annotations.VisibleForTesting;
+
// TODO: Needed for move to system service: import com.android.internal.R;
/**
@@ -52,7 +53,8 @@
* Calls to emergency numbers are still broadcast for informative purposes. The call is placed
* prior to sending ACTION_NEW_OUTGOING_CALL and cannot be redirected nor prevented.
*/
-class NewOutgoingCallIntentBroadcaster {
+@VisibleForTesting
+public class NewOutgoingCallIntentBroadcaster {
private static final String EXTRA_ACTUAL_NUMBER_TO_DIAL =
"android.telecom.extra.ACTUAL_NUMBER_TO_DIAL";
@@ -72,6 +74,7 @@
private final Call mCall;
private final Intent mIntent;
private final Context mContext;
+ private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
/*
* Whether or not the outgoing call intent originated from the default phone application. If
@@ -79,12 +82,15 @@
*/
private final boolean mIsDefaultOrSystemPhoneApp;
- NewOutgoingCallIntentBroadcaster(Context context, CallsManager callsManager, Call call,
- Intent intent, boolean isDefaultPhoneApp) {
+ @VisibleForTesting
+ public NewOutgoingCallIntentBroadcaster(Context context, CallsManager callsManager, Call call,
+ Intent intent, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
+ boolean isDefaultPhoneApp) {
mContext = context;
mCallsManager = callsManager;
mCall = call;
mIntent = intent;
+ mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
mIsDefaultOrSystemPhoneApp = isDefaultPhoneApp;
}
@@ -92,7 +98,7 @@
* Processes the result of the outgoing call broadcast intent, and performs callbacks to
* the OutgoingCallIntentBroadcasterListener as necessary.
*/
- private class NewOutgoingCallBroadcastIntentReceiver extends BroadcastReceiver {
+ public class NewOutgoingCallBroadcastIntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -111,7 +117,7 @@
if (resultNumber == null) {
Log.v(this, "Call cancelled (null number), returning...");
endEarly = true;
- } else if (PhoneNumberUtils.isPotentialLocalEmergencyNumber(
+ } else if (mPhoneNumberUtilsAdapter.isPotentialLocalEmergencyNumber(
mContext, resultNumber)) {
Log.w(this, "Cannot modify outgoing call to emergency number %s.",
resultNumber);
@@ -125,8 +131,10 @@
return;
}
- Uri resultHandleUri = Uri.fromParts(PhoneNumberUtils.isUriNumber(resultNumber) ?
- PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, resultNumber, null);
+ Uri resultHandleUri = Uri.fromParts(
+ mPhoneNumberUtilsAdapter.isUriNumber(resultNumber) ?
+ PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL,
+ resultNumber, null);
Uri originalUri = mIntent.getData();
@@ -166,7 +174,8 @@
* @return {@link DisconnectCause#NOT_DISCONNECTED} if the call succeeded, and an appropriate
* {@link DisconnectCause} if the call did not, describing why it failed.
*/
- int processIntent() {
+ @VisibleForTesting
+ public int processIntent() {
Log.v(this, "Processing call intent in OutgoingCallIntentBroadcaster.");
Intent intent = mIntent;
@@ -198,16 +207,16 @@
}
}
- String number = PhoneNumberUtils.getNumberFromIntent(intent, mContext);
+ String 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;
}
- boolean isUriNumber = PhoneNumberUtils.isUriNumber(number);
+ boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number);
if (!isUriNumber) {
- number = PhoneNumberUtils.convertKeypadLettersToDigits(number);
- number = PhoneNumberUtils.stripSeparators(number);
+ number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number);
+ number = mPhoneNumberUtilsAdapter.stripSeparators(number);
}
final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);
@@ -394,8 +403,8 @@
*/
private boolean isPotentialEmergencyNumber(String number) {
Log.v(this, "Checking restrictions for number : %s", Log.pii(number));
- return (number != null) && PhoneNumberUtils.isPotentialLocalEmergencyNumber(mContext,
- number);
+ return (number != null)
+ && mPhoneNumberUtilsAdapter.isPotentialLocalEmergencyNumber(mContext, number);
}
/**
diff --git a/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java b/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java
new file mode 100644
index 0000000..41284cb
--- /dev/null
+++ b/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 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.content.Context;
+import android.content.Intent;
+
+/**
+ * Interface to avoid static calls to PhoneNumberUtils. Add methods to this interface as needed for
+ * refactoring.
+ */
+public interface PhoneNumberUtilsAdapter {
+ boolean isPotentialLocalEmergencyNumber(Context context, String number);
+ boolean isUriNumber(String number);
+ String getNumberFromIntent(Intent intent, Context context);
+ String convertKeypadLettersToDigits(String number);
+ String stripSeparators(String number);
+}
diff --git a/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java b/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java
new file mode 100644
index 0000000..640d814
--- /dev/null
+++ b/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 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.content.Context;
+import android.content.Intent;
+import android.telephony.PhoneNumberUtils;
+
+public class PhoneNumberUtilsAdapterImpl implements PhoneNumberUtilsAdapter {
+ @Override
+ public boolean isPotentialLocalEmergencyNumber(Context context, String number) {
+ return PhoneNumberUtils.isPotentialLocalEmergencyNumber(context, number);
+ }
+
+ @Override
+ public boolean isUriNumber(String number) {
+ return PhoneNumberUtils.isUriNumber(number);
+ }
+
+ @Override
+ public String getNumberFromIntent(Intent intent, Context context) {
+ return PhoneNumberUtils.getNumberFromIntent(intent, context);
+ }
+
+ @Override
+ public String convertKeypadLettersToDigits(String number) {
+ return PhoneNumberUtils.convertKeypadLettersToDigits(number);
+ }
+
+ @Override
+ public String stripSeparators(String number) {
+ return PhoneNumberUtils.stripSeparators(number);
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/server/telecom/Timeouts.java b/src/com/android/server/telecom/Timeouts.java
index ba16990..cd1b9af 100644
--- a/src/com/android/server/telecom/Timeouts.java
+++ b/src/com/android/server/telecom/Timeouts.java
@@ -113,6 +113,7 @@
* perform a sweep to check and make sure that the session is still not incomplete (stale).
*/
public static long getStaleSessionCleanupTimeoutMillis(ContentResolver contentResolver) {
- return get(contentResolver, "stale_session_cleanup_timeout_millis", 30000L);
+ return get(contentResolver, "stale_session_cleanup_timeout_millis",
+ Log.DEFAULT_SESSION_TIMEOUT_MS);
}
}
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index d16f86b..b6ce48a 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -283,10 +283,11 @@
// Don't bother enforcing anything in mock.
}
- /**
- * Used to work around a Mockito/ART bug. If you remove any of these, tests will fail.
- */
- };
+ @Override
+ public void startActivityAsUser(Intent intent, UserHandle userHandle) {
+ // For capturing
+ }
+ }
public class FakeAudioManager extends AudioManager {
diff --git a/tests/src/com/android/server/telecom/tests/LogTest.java b/tests/src/com/android/server/telecom/tests/LogTest.java
index 673582a..98f4237 100644
--- a/tests/src/com/android/server/telecom/tests/LogTest.java
+++ b/tests/src/com/android/server/telecom/tests/LogTest.java
@@ -200,7 +200,7 @@
// Set to the default value of Timeouts.getStaleSessionCleanupTimeoutMillis without
// needing to query.
public long get() {
- return 30000;
+ return Log.DEFAULT_SESSION_TIMEOUT_MS;
}
};
}
diff --git a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
new file mode 100644
index 0000000..462593b
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2015 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.tests;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.telecom.GatewayInfo;
+import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
+import android.telephony.DisconnectCause;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.NewOutgoingCallIntentBroadcaster;
+import com.android.server.telecom.PhoneNumberUtilsAdapter;
+import com.android.server.telecom.PhoneNumberUtilsAdapterImpl;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNotNull;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase {
+ private static class ReceiverIntentPair {
+ public BroadcastReceiver receiver;
+ public Intent intent;
+
+ public ReceiverIntentPair(BroadcastReceiver receiver, Intent intent) {
+ this.receiver = receiver;
+ this.intent = intent;
+ }
+ }
+
+ @Mock private CallsManager mCallsManager;
+ @Mock private Call mCall;
+
+ private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapterSpy;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
+ mPhoneNumberUtilsAdapterSpy = spy(new PhoneNumberUtilsAdapterImpl());
+ }
+
+ public void testNullHandle() {
+ Intent intent = new Intent(Intent.ACTION_CALL, null);
+ int result = processIntent(intent, true);
+ assertEquals(DisconnectCause.INVALID_NUMBER, result);
+ verifyNoBroadcastSent();
+ verifyNoCallPlaced();
+ }
+
+ public void testVoicemailCall() {
+ String voicemailNumber = "voicemail:18005551234";
+ Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(voicemailNumber));
+ intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true);
+
+ int result = processIntent(intent, true);
+
+ assertEquals(DisconnectCause.NOT_DISCONNECTED, result);
+ verify(mCallsManager).placeOutgoingCall(eq(mCall), eq(Uri.parse(voicemailNumber)),
+ any(GatewayInfo.class), eq(true), eq(VideoProfile.STATE_AUDIO_ONLY));
+ }
+
+ public void testVoicemailCallWithBadAction() {
+ badCallActionHelper(Uri.parse("voicemail:18005551234"), DisconnectCause.OUTGOING_CANCELED);
+ }
+
+ public void testTelCallWithBadCallAction() {
+ badCallActionHelper(Uri.parse("tel:6505551234"), DisconnectCause.INVALID_NUMBER);
+ }
+
+ public void testSipCallWithBadCallAction() {
+ badCallActionHelper(Uri.parse("sip:testuser@testsite.com"), DisconnectCause.INVALID_NUMBER);
+ }
+
+ private void badCallActionHelper(Uri handle, int expectedCode) {
+ Intent intent = new Intent(Intent.ACTION_ALARM_CHANGED, handle);
+
+ int result = processIntent(intent, true);
+
+ assertEquals(expectedCode, result);
+ verifyNoBroadcastSent();
+ verifyNoCallPlaced();
+ }
+
+ public void testNoNumberSupplied() {
+ Uri handle = Uri.parse("tel:");
+ Intent intent = new Intent(Intent.ACTION_CALL, handle);
+
+ int result = processIntent(intent, true);
+
+ assertEquals(DisconnectCause.NO_PHONE_NUMBER_SUPPLIED, result);
+ verifyNoBroadcastSent();
+ verifyNoCallPlaced();
+ }
+
+
+ public void testEmergencyCallWithNonDefaultDialer() {
+ Uri handle = Uri.parse("tel:6505551911");
+ doReturn(true).when(mPhoneNumberUtilsAdapterSpy).isPotentialLocalEmergencyNumber(
+ any(Context.class), eq(handle.getSchemeSpecificPart()));
+ Intent intent = new Intent(Intent.ACTION_CALL, handle);
+
+ String ui_package_string = "sample_string_1";
+ String dialer_default_class_string = "sample_string_2";
+ mComponentContextFixture.putResource(R.string.ui_default_package, ui_package_string);
+ mComponentContextFixture.putResource(R.string.dialer_default_class,
+ dialer_default_class_string);
+
+ int result = processIntent(intent, false);
+
+ assertEquals(DisconnectCause.OUTGOING_CANCELED, result);
+ verifyNoBroadcastSent();
+ verifyNoCallPlaced();
+
+ ArgumentCaptor<Intent> dialerIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).startActivityAsUser(dialerIntentCaptor.capture(), any(UserHandle.class));
+ Intent dialerIntent = dialerIntentCaptor.getValue();
+ assertEquals(new ComponentName(ui_package_string, dialer_default_class_string),
+ dialerIntent.getComponent());
+ assertEquals(Intent.ACTION_DIAL, dialerIntent.getAction());
+ assertEquals(handle, dialerIntent.getData());
+ assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK, dialerIntent.getFlags());
+ }
+
+ public void testActionCallEmergencyCall() {
+ Uri handle = Uri.parse("tel:6505551911");
+ Intent intent = buildIntent(handle, Intent.ACTION_CALL, null);
+ emergencyCallTestHelper(intent, null);
+ }
+
+ public void testActionEmergencyWithEmergencyNumber() {
+ Uri handle = Uri.parse("tel:6505551911");
+ Intent intent = buildIntent(handle, Intent.ACTION_CALL_EMERGENCY, null);
+ emergencyCallTestHelper(intent, null);
+ }
+
+ public void testActionPrivCallWithEmergencyNumber() {
+ Uri handle = Uri.parse("tel:6505551911");
+ Intent intent = buildIntent(handle, Intent.ACTION_CALL_PRIVILEGED, null);
+ emergencyCallTestHelper(intent, null);
+ }
+
+ public void testEmergencyCallWithGatewayExtras() {
+ Uri handle = Uri.parse("tel:6505551911");
+ Bundle gatewayExtras = new Bundle();
+ gatewayExtras.putString(NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_PROVIDER_PACKAGE,
+ "sample1");
+ gatewayExtras.putString(NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_URI, "sample2");
+
+ Intent intent = buildIntent(handle, Intent.ACTION_CALL, gatewayExtras);
+ emergencyCallTestHelper(intent, gatewayExtras);
+ }
+
+ public void testActionEmergencyWithNonEmergencyNumber() {
+ Uri handle = Uri.parse("tel:6505551911");
+ 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);
+
+ assertEquals(DisconnectCause.OUTGOING_CANCELED, result);
+ verifyNoCallPlaced();
+ verifyNoBroadcastSent();
+ }
+
+ private void emergencyCallTestHelper(Intent intent, Bundle expectedAdditionalExtras) {
+ Uri handle = intent.getData();
+ int videoState = VideoProfile.STATE_BIDIRECTIONAL;
+ boolean isSpeakerphoneOn = true;
+ doReturn(true).when(mPhoneNumberUtilsAdapterSpy).isPotentialLocalEmergencyNumber(
+ 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);
+
+ assertEquals(DisconnectCause.NOT_DISCONNECTED, result);
+ verify(mCallsManager).placeOutgoingCall(eq(mCall), eq(handle), isNull(GatewayInfo.class),
+ eq(isSpeakerphoneOn), eq(videoState));
+
+ Bundle expectedExtras = createNumberExtras(handle.getSchemeSpecificPart());
+ if (expectedAdditionalExtras != null) {
+ expectedExtras.putAll(expectedAdditionalExtras);
+ }
+ BroadcastReceiver receiver = verifyBroadcastSent(handle.getSchemeSpecificPart(),
+ expectedExtras).receiver;
+ assertNull(receiver);
+ }
+
+ public void testUnmodifiedRegularCall() {
+ Uri handle = Uri.parse("tel:6505551234");
+ Intent callIntent = buildIntent(handle, Intent.ACTION_CALL, null);
+ ReceiverIntentPair result = regularCallTestHelper(callIntent, null);
+
+ result.receiver.setResultData(
+ result.intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
+
+ result.receiver.onReceive(mContext, result.intent);
+
+ verify(mCallsManager).placeOutgoingCall(eq(mCall), eq(handle), isNull(GatewayInfo.class),
+ eq(true), eq(VideoProfile.STATE_BIDIRECTIONAL));
+ }
+
+ public void testUnmodifiedSipCall() {
+ Uri handle = Uri.parse("sip:test@test.com");
+ Intent callIntent = buildIntent(handle, Intent.ACTION_CALL, null);
+ ReceiverIntentPair result = regularCallTestHelper(callIntent, null);
+
+ result.receiver.setResultData(
+ result.intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
+
+ result.receiver.onReceive(mContext, result.intent);
+
+ Uri encHandle = Uri.fromParts(handle.getScheme(),
+ handle.getSchemeSpecificPart(), null);
+ verify(mCallsManager).placeOutgoingCall(eq(mCall), eq(encHandle), isNull(GatewayInfo.class),
+ eq(true), eq(VideoProfile.STATE_BIDIRECTIONAL));
+ }
+
+ public void testCallWithGatewayInfo() {
+ Uri handle = Uri.parse("tel:6505551234");
+ Intent callIntent = buildIntent(handle, Intent.ACTION_CALL, null);
+
+ callIntent.putExtra(NewOutgoingCallIntentBroadcaster
+ .EXTRA_GATEWAY_PROVIDER_PACKAGE, "sample1");
+ callIntent.putExtra(NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_URI, "sample2");
+ ReceiverIntentPair result = regularCallTestHelper(callIntent, callIntent.getExtras());
+
+ result.receiver.setResultData(
+ result.intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
+
+ result.receiver.onReceive(mContext, result.intent);
+
+ verify(mCallsManager).placeOutgoingCall(eq(mCall), eq(handle),
+ isNotNull(GatewayInfo.class), eq(true), eq(VideoProfile.STATE_BIDIRECTIONAL));
+ }
+
+ public void testCallNumberModifiedToNull() {
+ Uri handle = Uri.parse("tel:6505551234");
+ Intent callIntent = buildIntent(handle, Intent.ACTION_CALL, null);
+ ReceiverIntentPair result = regularCallTestHelper(callIntent, null);
+
+ result.receiver.setResultData(null);
+
+ result.receiver.onReceive(mContext, result.intent);
+ verifyNoCallPlaced();
+ verify(mCall).disconnect(true);
+ }
+
+ public void testCallModifiedToEmergency() {
+ Uri handle = Uri.parse("tel:6505551234");
+ Intent callIntent = buildIntent(handle, Intent.ACTION_CALL, null);
+ ReceiverIntentPair result = regularCallTestHelper(callIntent, null);
+
+ String newEmergencyNumber = "1234567890";
+ result.receiver.setResultData(newEmergencyNumber);
+
+ doReturn(true).when(mPhoneNumberUtilsAdapterSpy).isPotentialLocalEmergencyNumber(
+ any(Context.class), eq(newEmergencyNumber));
+ result.receiver.onReceive(mContext, result.intent);
+ verify(mCall).disconnect(true);
+ }
+
+ private ReceiverIntentPair regularCallTestHelper(Intent intent,
+ Bundle expectedAdditionalExtras) {
+ Uri handle = intent.getData();
+ int videoState = VideoProfile.STATE_BIDIRECTIONAL;
+ boolean isSpeakerphoneOn = true;
+ intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, isSpeakerphoneOn);
+ intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
+
+ int result = processIntent(intent, true);
+
+ assertEquals(DisconnectCause.NOT_DISCONNECTED, result);
+ Bundle expectedExtras = createNumberExtras(handle.getSchemeSpecificPart());
+ if (expectedAdditionalExtras != null) {
+ expectedExtras.putAll(expectedAdditionalExtras);
+ }
+ return verifyBroadcastSent(handle.getSchemeSpecificPart(), expectedExtras);
+ }
+
+ private Intent buildIntent(Uri handle, String action, Bundle extras) {
+ Intent i = new Intent(action, handle);
+ if (extras != null) {
+ i.putExtras(extras);
+ }
+ return i;
+ }
+
+ private int processIntent(Intent intent,
+ boolean isDefaultPhoneApp) {
+ NewOutgoingCallIntentBroadcaster b = new NewOutgoingCallIntentBroadcaster(
+ mContext, mCallsManager, mCall, intent, mPhoneNumberUtilsAdapterSpy,
+ isDefaultPhoneApp);
+ return b.processIntent();
+ }
+
+ private ReceiverIntentPair verifyBroadcastSent(String number, Bundle expectedExtras) {
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+
+ verify(mContext).sendOrderedBroadcastAsUser(
+ intentCaptor.capture(),
+ eq(UserHandle.CURRENT),
+ eq(Manifest.permission.PROCESS_OUTGOING_CALLS),
+ eq(AppOpsManager.OP_PROCESS_OUTGOING_CALLS),
+ receiverCaptor.capture(),
+ isNull(Handler.class),
+ eq(Activity.RESULT_OK),
+ eq(number),
+ isNull(Bundle.class));
+
+ Intent capturedIntent = intentCaptor.getValue();
+ assertEquals(Intent.ACTION_NEW_OUTGOING_CALL, capturedIntent.getAction());
+ assertEquals(Intent.FLAG_RECEIVER_FOREGROUND, capturedIntent.getFlags());
+ assertTrue(areBundlesEqual(expectedExtras, capturedIntent.getExtras()));
+
+ BroadcastReceiver receiver = receiverCaptor.getValue();
+ if (receiver != null) {
+ receiver.setPendingResult(
+ new BroadcastReceiver.PendingResult(0, "", null, 0, true, false, null, 0, 0));
+ }
+
+ return new ReceiverIntentPair(receiver, capturedIntent);
+ }
+
+ private Bundle createNumberExtras(String number) {
+ Bundle b = new Bundle();
+ b.putString(Intent.EXTRA_PHONE_NUMBER, number);
+ return b;
+ }
+
+ private void verifyNoCallPlaced() {
+ verify(mCallsManager, never()).placeOutgoingCall(any(Call.class), any(Uri.class),
+ any(GatewayInfo.class), anyBoolean(), anyInt());
+ }
+
+ private void verifyNoBroadcastSent() {
+ verify(mContext, never()).sendOrderedBroadcastAsUser(
+ any(Intent.class),
+ any(UserHandle.class),
+ anyString(),
+ anyInt(),
+ any(BroadcastReceiver.class),
+ any(Handler.class),
+ anyInt(),
+ anyString(),
+ any(Bundle.class));
+ }
+
+ private static boolean areBundlesEqual(Bundle b1, Bundle b2) {
+ for (String key1 : b1.keySet()) {
+ if (!b1.get(key1).equals(b2.get(key1))) {
+ return false;
+ }
+ }
+
+ for (String key2 : b2.keySet()) {
+ if (!b2.get(key2).equals(b1.get(key2))) {
+ return false;
+ }
+ }
+ return true;
+ }
+}