Merge "Speculative fix for flaky CallAudioRouteStateMachine tests"
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 9eb8aea..227d5f5 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -424,7 +424,8 @@
@VisibleForTesting
public boolean startRinging() {
- return mRinger.startRinging(mForegroundCall);
+ return mRinger.startRinging(mForegroundCall,
+ mCallAudioRouteStateMachine.isHfpDeviceAvailable());
}
@VisibleForTesting
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index 7dfd78c..365ef4d 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -1323,6 +1323,10 @@
getHandler().getLooper().dump(pw::println, "");
}
+ public boolean isHfpDeviceAvailable() {
+ return mBluetoothRouteManager.isBluetoothAvailable();
+ }
+
/**
* Sets whether notifications should be suppressed or not. Used when in a call to ensure the
* device will not vibrate due to notifications.
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index fc22127..e89075d 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -122,6 +122,30 @@
private static final String TAG = "CallsManager";
+ /**
+ * Call filter specifier used with
+ * {@link #getNumCallsWithState(int, Call, PhoneAccountHandle, int...)} to indicate only
+ * self-managed calls should be included.
+ */
+ private static final int CALL_FILTER_SELF_MANAGED = 1;
+
+ /**
+ * Call filter specifier used with
+ * {@link #getNumCallsWithState(int, Call, PhoneAccountHandle, int...)} to indicate only
+ * managed calls should be included.
+ */
+ private static final int CALL_FILTER_MANAGED = 2;
+
+ /**
+ * Call filter specifier used with
+ * {@link #getNumCallsWithState(int, Call, PhoneAccountHandle, int...)} to indicate both managed
+ * and self-managed calls should be included.
+ */
+ private static final int CALL_FILTER_ALL = 3;
+
+ private static final String PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION =
+ "android.permission.PROCESS_PHONE_ACCOUNT_REGISTRATION";
+
private static final int HANDLER_WAIT_TIMEOUT = 10000;
private static final int MAXIMUM_LIVE_CALLS = 1;
private static final int MAXIMUM_HOLD_CALLS = 1;
@@ -135,10 +159,25 @@
{CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
CallState.PULLING};
+ /**
+ * These states are used by {@link #makeRoomForOutgoingCall(Call, boolean)} to determine which
+ * call should be ended first to make room for a new outgoing call.
+ */
private static final int[] LIVE_CALL_STATES =
{CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
CallState.PULLING, CallState.ACTIVE};
+ /**
+ * These states determine which calls will cause {@link TelecomManager#isInCall()} or
+ * {@link TelecomManager#isInManagedCall()} to return true.
+ *
+ * See also {@link PhoneStateBroadcaster}, which considers a similar set of states as being
+ * off-hook.
+ */
+ public static final int[] ONGOING_CALL_STATES =
+ {CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, CallState.PULLING, CallState.ACTIVE,
+ CallState.ON_HOLD, CallState.RINGING};
+
private static final int[] ANY_CALL_STATE =
{CallState.NEW, CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
CallState.RINGING, CallState.ACTIVE, CallState.ON_HOLD, CallState.DISCONNECTED,
@@ -225,6 +264,21 @@
private Runnable mStopTone;
/**
+ * Listener to PhoneAccountRegistrar events.
+ */
+ private PhoneAccountRegistrar.Listener mPhoneAccountListener =
+ new PhoneAccountRegistrar.Listener() {
+ public void onPhoneAccountRegistered(PhoneAccountRegistrar registrar,
+ PhoneAccountHandle handle) {
+ broadcastRegisterIntent(handle);
+ }
+ public void onPhoneAccountUnRegistered(PhoneAccountRegistrar registrar,
+ PhoneAccountHandle handle) {
+ broadcastUnregisterIntent(handle);
+ }
+ };
+
+ /**
* Initializes the required Telecom components.
*/
CallsManager(
@@ -252,6 +306,7 @@
mContactsAsyncHelper = contactsAsyncHelper;
mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory;
mPhoneAccountRegistrar = phoneAccountRegistrar;
+ mPhoneAccountRegistrar.addListener(mPhoneAccountListener);
mMissedCallNotifier = missedCallNotifier;
StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this);
mWiredHeadsetManager = wiredHeadsetManager;
@@ -2105,16 +2160,40 @@
@VisibleForTesting
public int getNumCallsWithState(final boolean isSelfManaged, Call excludeCall,
- PhoneAccountHandle phoneAccountHandle, int... states) {
+ PhoneAccountHandle phoneAccountHandle, int... states) {
+ return getNumCallsWithState(isSelfManaged ? CALL_FILTER_SELF_MANAGED : CALL_FILTER_MANAGED,
+ excludeCall, phoneAccountHandle, states);
+ }
+
+ /**
+ * Determines the number of calls matching the specified criteria.
+ * @param callFilter indicates whether to include just managed calls
+ * ({@link #CALL_FILTER_MANAGED}), self-managed calls
+ * ({@link #CALL_FILTER_SELF_MANAGED}), or all calls
+ * ({@link #CALL_FILTER_ALL}).
+ * @param excludeCall Where {@code non-null}, this call is excluded from the count.
+ * @param phoneAccountHandle Where {@code non-null}, calls for this {@link PhoneAccountHandle}
+ * are excluded from the count.
+ * @param states The list of {@link CallState}s to include in the count.
+ * @return Count of calls matching criteria.
+ */
+ @VisibleForTesting
+ public int getNumCallsWithState(final int callFilter, Call excludeCall,
+ PhoneAccountHandle phoneAccountHandle, int... states) {
Set<Integer> desiredStates = IntStream.of(states).boxed().collect(Collectors.toSet());
Stream<Call> callsStream = mCalls.stream()
.filter(call -> desiredStates.contains(call.getState()) &&
- call.getParentCall() == null && !call.isExternalCall() &&
- call.isSelfManaged() == isSelfManaged);
+ call.getParentCall() == null && !call.isExternalCall());
- // If a call to exclude was specifeid, filter it out.
+ if (callFilter == CALL_FILTER_MANAGED) {
+ callsStream = callsStream.filter(call -> !call.isSelfManaged());
+ } else if (callFilter == CALL_FILTER_SELF_MANAGED) {
+ callsStream = callsStream.filter(call -> call.isSelfManaged());
+ }
+
+ // If a call to exclude was specified, filter it out.
if (excludeCall != null) {
callsStream = callsStream.filter(call -> call != excludeCall);
}
@@ -2205,18 +2284,31 @@
}
/**
+ * Determines if there are any ongoing managed or self-managed calls.
+ * Note: The {@link #ONGOING_CALL_STATES} are
+ * @return {@code true} if there are ongoing managed or self-managed calls, {@code false}
+ * otherwise.
+ */
+ public boolean hasOngoingCalls() {
+ return getNumCallsWithState(
+ CALL_FILTER_ALL, null /* excludeCall */,
+ null /* phoneAccountHandle */,
+ ONGOING_CALL_STATES) > 0;
+ }
+
+ /**
* Determines if there are any ongoing managed calls.
* @return {@code true} if there are ongoing managed calls, {@code false} otherwise.
*/
public boolean hasOngoingManagedCalls() {
return getNumCallsWithState(
- false /* isSelfManaged */, null /* excludeCall */,
+ CALL_FILTER_MANAGED, null /* excludeCall */,
null /* phoneAccountHandle */,
- LIVE_CALL_STATES) > 0;
+ ONGOING_CALL_STATES) > 0;
}
/**
- * Deteremines if the system incoming call UI should be shown.
+ * Determines if the system incoming call UI should be shown.
* The system incoming call UI will be shown if the new incoming call is self-managed, and there
* are ongoing calls for another PhoneAccount.
* @param incomingCall The incoming call.
@@ -2664,4 +2756,48 @@
service.createConnectionFailed(call);
}
}
+
+ private void broadcastUnregisterIntent(PhoneAccountHandle accountHandle) {
+ Intent intent =
+ new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED);
+ intent.putExtra(
+ TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
+ Log.i(this, "Sending phone-account %s unregistered intent as user", accountHandle);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION);
+
+ String dialerPackage = mDefaultDialerCache.getDefaultDialerApplication(
+ getCurrentUserHandle().getIdentifier());
+ if (!TextUtils.isEmpty(dialerPackage)) {
+ Intent directedIntent = new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED)
+ .setPackage(dialerPackage);
+ directedIntent.putExtra(
+ TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
+ Log.i(this, "Sending phone-account unregistered intent to default dialer");
+ mContext.sendBroadcastAsUser(directedIntent, UserHandle.ALL, null);
+ }
+ return ;
+ }
+
+ private void broadcastRegisterIntent(PhoneAccountHandle accountHandle) {
+ Intent intent = new Intent(
+ TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED);
+ intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+ accountHandle);
+ Log.i(this, "Sending phone-account %s registered intent as user", accountHandle);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION);
+
+ String dialerPackage = mDefaultDialerCache.getDefaultDialerApplication(
+ getCurrentUserHandle().getIdentifier());
+ if (!TextUtils.isEmpty(dialerPackage)) {
+ Intent directedIntent = new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED)
+ .setPackage(dialerPackage);
+ directedIntent.putExtra(
+ TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
+ Log.i(this, "Sending phone-account registered intent to default dialer");
+ mContext.sendBroadcastAsUser(directedIntent, UserHandle.ALL, null);
+ }
+ return ;
+ }
}
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 1c75c50..169ebd7 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -61,7 +61,7 @@
* can send updates to the in-call app. This class is created and owned by CallsManager and retains
* a binding to the {@link IInCallService} (implemented by the in-call app).
*/
-public final class InCallController extends CallsManagerListenerBase {
+public class InCallController extends CallsManagerListenerBase {
public class InCallServiceConnection {
/**
@@ -212,7 +212,8 @@
return CONNECTION_SUCCEEDED;
}
- if (call.isSelfManaged() && !mInCallServiceInfo.isSelfManagedCallsSupported()) {
+ if (call != null && call.isSelfManaged() &&
+ !mInCallServiceInfo.isSelfManagedCallsSupported()) {
Log.i(this, "Skipping binding to %s - doesn't support self-mgd calls",
mInCallServiceInfo);
mIsConnected = false;
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index af633fb..8cdec30 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -120,6 +120,10 @@
public void onAccountsChanged(PhoneAccountRegistrar registrar) {}
public void onDefaultOutgoingChanged(PhoneAccountRegistrar registrar) {}
public void onSimCallManagerChanged(PhoneAccountRegistrar registrar) {}
+ public void onPhoneAccountRegistered(PhoneAccountRegistrar registrar,
+ PhoneAccountHandle handle) {}
+ public void onPhoneAccountUnRegistered(PhoneAccountRegistrar registrar,
+ PhoneAccountHandle handle) {}
}
/**
@@ -652,14 +656,17 @@
// !!! IMPORTANT !!! It is important that we do not read the enabled state that the
// source app provides or else an third party app could enable itself.
boolean isEnabled = false;
+ boolean isNewAccount;
PhoneAccount oldAccount = getPhoneAccountUnchecked(account.getAccountHandle());
if (oldAccount != null) {
mState.accounts.remove(oldAccount);
isEnabled = oldAccount.isEnabled();
- Log.i(this, getAccountDiffString(account, oldAccount));
+ Log.i(this, "Modify account: %s", getAccountDiffString(account, oldAccount));
+ isNewAccount = false;
} else {
Log.i(this, "New phone account registered: " + account);
+ isNewAccount = true;
}
// When registering a self-managed PhoneAccount we enforce the rule that the label that the
@@ -695,6 +702,9 @@
write();
fireAccountsChanged();
+ if (isNewAccount) {
+ fireAccountRegistered(account.getAccountHandle());
+ }
}
public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
@@ -703,6 +713,7 @@
if (mState.accounts.remove(account)) {
write();
fireAccountsChanged();
+ fireAccountUnRegistered(accountHandle);
}
}
}
@@ -748,6 +759,18 @@
}
}
+ private void fireAccountRegistered(PhoneAccountHandle handle) {
+ for (Listener l : mListeners) {
+ l.onPhoneAccountRegistered(this, handle);
+ }
+ }
+
+ private void fireAccountUnRegistered(PhoneAccountHandle handle) {
+ for (Listener l : mListeners) {
+ l.onPhoneAccountUnRegistered(this, handle);
+ }
+ }
+
private void fireAccountsChanged() {
for (Listener l : mListeners) {
l.onAccountsChanged(this);
@@ -771,7 +794,6 @@
Log.piiHandle(account2.getAddress()));
appendDiff(sb, "cap", account1.getCapabilities(), account2.getCapabilities());
appendDiff(sb, "hl", account1.getHighlightColor(), account2.getHighlightColor());
- appendDiff(sb, "icon", account1.getIcon(), account2.getIcon());
appendDiff(sb, "lbl", account1.getLabel(), account2.getLabel());
appendDiff(sb, "desc", account1.getShortDescription(), account2.getShortDescription());
appendDiff(sb, "subAddr", Log.piiHandle(account1.getSubscriptionAddress()),
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index c5817e7..d955227 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -97,41 +97,44 @@
mInCallController = inCallController;
}
- public boolean startRinging(Call foregroundCall) {
- AudioManager audioManager =
- (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- boolean isRingerAudible = audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0;
-
- if (mSystemSettingsUtil.isTheaterModeOn(mContext)) {
- return false;
- }
-
+ public boolean startRinging(Call foregroundCall, boolean isHfpDeviceAttached) {
if (foregroundCall == null) {
Log.wtf(this, "startRinging called with null foreground call.");
return false;
}
- if (mInCallController.doesConnectedDialerSupportRinging()) {
- Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING);
- return isRingerAudible;
- }
+ AudioManager audioManager =
+ (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ boolean isVolumeOverZero = audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0;
+ boolean shouldRingForContact = shouldRingForContact(foregroundCall.getContactUri());
+ boolean isRingtonePresent = !(mRingtoneFactory.getRingtone(foregroundCall) == null);
+ boolean isSelfManaged = foregroundCall.isSelfManaged();
- if (foregroundCall.isSelfManaged()) {
- Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Self-managed");
- return false;
+ boolean isRingerAudible = isVolumeOverZero && shouldRingForContact && isRingtonePresent;
+ // Acquire audio focus under any of the following conditions:
+ // 1. Should ring for contact and there's an HFP device attached
+ // 2. Volume is over zero, we should ring for the contact, and there's a audible ringtone
+ // present.
+ // 3. The call is self-managed.
+ boolean shouldAcquireAudioFocus =
+ isRingerAudible || (isHfpDeviceAttached && shouldRingForContact) || isSelfManaged;
+
+ // Don't do call waiting operations or vibration unless these are false.
+ boolean isTheaterModeOn = mSystemSettingsUtil.isTheaterModeOn(mContext);
+ boolean letDialerHandleRinging = mInCallController.doesConnectedDialerSupportRinging();
+ boolean endEarly = isTheaterModeOn || letDialerHandleRinging || isSelfManaged;
+
+ if (endEarly) {
+ if (letDialerHandleRinging) {
+ Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING);
+ }
+ Log.i(this, "Ending early -- isTheaterModeOn=%s, letDialerHandleRinging=%s, " +
+ "isSelfManaged=%s", isTheaterModeOn, letDialerHandleRinging, isSelfManaged);
+ return shouldAcquireAudioFocus;
}
stopCallWaiting();
- if (!shouldRingForContact(foregroundCall.getContactUri())) {
- return false;
- }
-
- // Don't ring/acquire focus if there is no ringtone
- if (mRingtoneFactory.getRingtone(foregroundCall) == null) {
- isRingerAudible = false;
- }
-
if (isRingerAudible) {
mRingingCall = foregroundCall;
Log.addEvent(foregroundCall, LogUtils.Events.START_RINGER);
@@ -141,10 +144,12 @@
// request the custom ringtone from the call and expect it to be current.
mRingtonePlayer.play(mRingtoneFactory, foregroundCall);
} else {
- Log.i(this, "startRingingOrCallWaiting, skipping because volume is 0");
+ Log.i(this, "startRinging: skipping because ringer would not be audible. " +
+ "isVolumeOverZero=%s, shouldRingForContact=%s, isRingtonePresent=%s",
+ isVolumeOverZero, shouldRingForContact, isRingtonePresent);
}
- if (shouldVibrate(mContext, foregroundCall) && !mIsVibrating) {
+ if (shouldVibrate(mContext, foregroundCall) && !mIsVibrating && shouldRingForContact) {
mVibratingCall = foregroundCall;
mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,
VIBRATION_ATTRIBUTES);
@@ -153,7 +158,7 @@
Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION, "already vibrating");
}
- return isRingerAudible;
+ return shouldAcquireAudioFocus;
}
public void startCallWaiting(Call call) {
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index ae80350..40ba21d 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -48,6 +48,7 @@
import android.telecom.VideoProfile;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.EventLog;
// TODO: Needed for move to system service: import com.android.internal.R;
@@ -78,8 +79,6 @@
}
}
- private static final String PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION =
- "android.permission.PROCESS_PHONE_ACCOUNT_REGISTRATION";
private static final int DEFAULT_VIDEO_STATE = -1;
private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {
@@ -400,17 +399,9 @@
enforceRegisterMultiUser();
}
enforceUserHandleMatchesCaller(account.getAccountHandle());
- mPhoneAccountRegistrar.registerPhoneAccount(account);
- // Broadcast an intent indicating the phone account which was registered.
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
- Intent intent = new Intent(
- TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED);
- intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
- account.getAccountHandle());
- Log.i(this, "Sending phone-account registered intent as user");
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
- PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION);
+ mPhoneAccountRegistrar.registerPhoneAccount(account);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -432,18 +423,9 @@
enforcePhoneAccountModificationForPackage(
accountHandle.getComponentName().getPackageName());
enforceUserHandleMatchesCaller(accountHandle);
- mPhoneAccountRegistrar.unregisterPhoneAccount(accountHandle);
-
- // Broadcast an intent indicating the phone account which was unregistered.
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
- Intent intent =
- new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED);
- intent.putExtra(
- TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
- Log.i(this, "Sending phone-account unregistered intent as user");
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
- PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION);
+ mPhoneAccountRegistrar.unregisterPhoneAccount(accountHandle);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -667,9 +649,7 @@
}
synchronized (mLock) {
- final int callState = mCallsManager.getCallState();
- return callState == TelephonyManager.CALL_STATE_OFFHOOK
- || callState == TelephonyManager.CALL_STATE_RINGING;
+ return mCallsManager.hasOngoingCalls();
}
} finally {
Log.endSession();
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index 494089b..e3d99d9 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -29,6 +29,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.IState;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.telecom.BluetoothHeadsetProxy;
@@ -43,6 +44,8 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class BluetoothRouteManager extends StateMachine {
private static final String LOG_TAG = BluetoothRouteManager.class.getSimpleName();
@@ -581,8 +584,27 @@
return mDeviceManager.getNumConnectedDevices() > 0;
}
+ /**
+ * This method needs be synchronized with the local looper because getCurrentState() depends
+ * on the internal state of the state machine being consistent. Therefore, there may be a
+ * delay when calling this method.
+ * @return
+ */
public boolean isBluetoothAudioConnectedOrPending() {
- return getCurrentState() != mAudioOffState;
+ IState[] state = new IState[] {null};
+ CountDownLatch latch = new CountDownLatch(1);
+ Runnable r = () -> {
+ state[0] = getCurrentState();
+ latch.countDown();
+ };
+ sendMessage(RUN_RUNNABLE, r);
+ try {
+ latch.await(1000, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Log.w(LOG_TAG, "isBluetoothAudioConnectedOrPending -- interrupted getting state");
+ return false;
+ }
+ return (state[0] != null) && (state[0] != mAudioOffState);
}
/**
diff --git a/testapps/AndroidManifest.xml b/testapps/AndroidManifest.xml
index ec49696..ba8db53 100644
--- a/testapps/AndroidManifest.xml
+++ b/testapps/AndroidManifest.xml
@@ -66,7 +66,8 @@
<intent-filter>
<action android:name="android.server.telecom.testapps.ACTION_SEND_UPDATE_REQUEST_FROM_TEST_INCALL_SERVICE"/>
<action android:name="android.server.telecom.testapps.ACTION_SEND_UPGRADE_RESPONSE"/>
- <data android:scheme="int" />
+ <action android:name="android.telecom.action.PHONE_ACCOUNT_REGISTERED"/>
+ <action android:name="android.telecom.action.PHONE_ACCOUNT_UNREGISTERED"/>
</intent-filter>
</receiver>
@@ -167,6 +168,16 @@
</intent-filter>
</activity>
+ <activity android:name="com.android.server.telecom.testapps.TestUssdActivity"
+ android:label="@string/UssdUiAppLabel"
+ android:launchMode="singleInstance">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
<activity android:name="com.android.server.telecom.testapps.SelfManagedCallingActivity"
android:label="@string/selfManagedCallingActivityLabel"
android:process="com.android.server.telecom.testapps.SelfMangingCallingApp"
diff --git a/testapps/res/layout/testussd_main.xml b/testapps/res/layout/testussd_main.xml
new file mode 100644
index 0000000..b4d67b0
--- /dev/null
+++ b/testapps/res/layout/testussd_main.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+ <EditText
+ android:id="@+id/number"
+ android:inputType="number"
+ android:layout_width="200dp"
+ android:layout_height="wrap_content" />
+ <Button
+ android:id="@+id/place_ussd_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeUssdButton" />
+</LinearLayout>
diff --git a/testapps/res/values/donottranslate_strings.xml b/testapps/res/values/donottranslate_strings.xml
index 9dc7cae..d4a011a 100644
--- a/testapps/res/values/donottranslate_strings.xml
+++ b/testapps/res/values/donottranslate_strings.xml
@@ -88,4 +88,7 @@
<item>The FCC has mandated that I respond... I will do so begrudgingly</item>
<item>😂😂😂💯</item>
</string-array>
+
+ <string name="UssdUiAppLabel">Test Ussd UI</string>
+ <string name="placeUssdButton">Send USSD</string>
</resources>
diff --git a/testapps/src/com/android/server/telecom/testapps/TestInCallServiceBroadcastReceiver.java b/testapps/src/com/android/server/telecom/testapps/TestInCallServiceBroadcastReceiver.java
index b6902bf..3371060 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestInCallServiceBroadcastReceiver.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestInCallServiceBroadcastReceiver.java
@@ -19,6 +19,7 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.telecom.TelecomManager;
import android.util.Log;
/**
@@ -56,6 +57,12 @@
} else if (ACTION_SEND_UPGRADE_RESPONSE.equals(action)) {
final int videoState = Integer.parseInt(intent.getData().getSchemeSpecificPart());
TestCallList.getInstance().sendUpgradeToVideoResponse(videoState);
+ } else if (TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED.equals(action)) {
+ Log.i(TAG, "onReceive: registered " + intent.getExtras().get(
+ TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE));
+ } else if (TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED.equals(action)) {
+ Log.i(TAG, "onReceive: unregistered " + intent.getExtras().get(
+ TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE));
}
}
}
diff --git a/testapps/src/com/android/server/telecom/testapps/TestUssdActivity.java b/testapps/src/com/android/server/telecom/testapps/TestUssdActivity.java
new file mode 100644
index 0000000..3b1f4e9
--- /dev/null
+++ b/testapps/src/com/android/server/telecom/testapps/TestUssdActivity.java
@@ -0,0 +1,80 @@
+package com.android.server.telecom.testapps;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.telephony.TelephonyManager;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.EditText;
+import android.widget.Toast;
+
+public class TestUssdActivity extends Activity {
+
+ private EditText mUssdNumberView;
+ private static Context context;
+ public static final String LOG_TAG = "TestUssdActivity";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ TestUssdActivity.context = getApplicationContext();
+
+ setContentView(R.layout.testussd_main);
+ findViewById(R.id.place_ussd_button).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ placeUssdRequest();
+ }
+ });
+
+ mUssdNumberView = (EditText) findViewById(R.id.number);
+ }
+
+ public static final class OnReceiveUssdResponseCallback extends
+ TelephonyManager.OnReceiveUssdResponseCallback {
+
+ OnReceiveUssdResponseCallback() {
+ }
+
+ public void onReceiveUssdResponse(String req, CharSequence message) {
+ Log.i(LOG_TAG, "USSD Success:::" + req + "," + message);
+ showToast("USSD Response Successly received for code:" + req + "," + message);
+ }
+
+ public void onReceiveUssdResponseFailed(String req, int resultCode) {
+ Log.i(LOG_TAG, "USSD Fail:::" + req + "," + resultCode);
+ showToast("USSD Response failed for code:" + req + "," + resultCode);
+ }
+ }
+
+ private void placeUssdRequest() {
+
+ String mUssdNumber = mUssdNumberView.getText().toString();
+ if (mUssdNumber.equals("") || mUssdNumber == null) {
+ mUssdNumber = "932";
+ }
+ mUssdNumber = "#" + mUssdNumber + "#";
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+ try {
+ Handler h = new Handler(Looper.getMainLooper());
+ OnReceiveUssdResponseCallback receiveUssdResponseCallback =
+ new OnReceiveUssdResponseCallback();
+
+ telephonyManager.sendUssdRequest(mUssdNumber, receiveUssdResponseCallback, h);
+
+ } catch (SecurityException e) {
+ showToast("Permission check failed");
+ return;
+ }
+ }
+
+ private static void showToast(String message) {
+ Toast.makeText(TestUssdActivity.context, message, Toast.LENGTH_SHORT).show();
+ }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java b/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java
index bba7c5a..300415a 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java
@@ -48,6 +48,7 @@
import java.util.ArrayList;
import java.util.LinkedList;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyChar;
@@ -190,7 +191,7 @@
Call mockCall = createForegroundCall();
PhoneAccount fakePhoneAccount = makeQuickAccount("id0", TEST_ACCOUNT_INDEX);
when(mMockPhoneAccountRegistrar.getPhoneAccountOfCurrentUser(
- any(PhoneAccountHandle.class))).thenReturn(fakePhoneAccount);
+ nullable(PhoneAccountHandle.class))).thenReturn(fakePhoneAccount);
String networkOperator = mBluetoothPhoneService.mBinder.getNetworkOperator();
@@ -211,7 +212,7 @@
Call mockCall = createForegroundCall();
PhoneAccount fakePhoneAccount = makeQuickAccount("id0", TEST_ACCOUNT_INDEX);
when(mMockPhoneAccountRegistrar.getPhoneAccountOfCurrentUser(
- any(PhoneAccountHandle.class))).thenReturn(fakePhoneAccount);
+ nullable(PhoneAccountHandle.class))).thenReturn(fakePhoneAccount);
String subscriberNumber = mBluetoothPhoneService.mBinder.getSubscriberNumber();
@@ -223,9 +224,9 @@
Call mockCall = createForegroundCall();
String fakeNumber = "8675309";
when(mMockPhoneAccountRegistrar.getPhoneAccountOfCurrentUser(
- any(PhoneAccountHandle.class))).thenReturn(null);
+ nullable(PhoneAccountHandle.class))).thenReturn(null);
when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(
- any(PhoneAccountHandle.class))).thenReturn(null);
+ nullable(PhoneAccountHandle.class))).thenReturn(null);
when(TelephonyManager.from(mContext).getLine1Number()).thenReturn(fakeNumber);
String subscriberNumber = mBluetoothPhoneService.mBinder.getSubscriberNumber();
@@ -297,7 +298,7 @@
mBluetoothPhoneService.mCallsManagerListener.onIsConferencedChanged(parentCall);
// Make sure the call has only occurred collectively 2 times (not on the third)
verify(mMockBluetoothHeadset, times(2)).phoneStateChanged(any(int.class),
- any(int.class), any(int.class), any(String.class), any(int.class));
+ any(int.class), any(int.class), nullable(String.class), any(int.class));
}
@MediumTest
@@ -401,7 +402,7 @@
"5550000", PhoneNumberUtils.TOA_Unknown);
verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
verify(mMockBluetoothHeadset, times(2)).clccResponse(anyInt(),
- anyInt(), anyInt(), anyInt(), anyBoolean(), anyString(), anyInt());
+ anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
}
@MediumTest
@@ -416,7 +417,7 @@
mBluetoothPhoneService.mBinder.listCurrentCalls();
verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
verify(mMockBluetoothHeadset, times(1)).clccResponse(anyInt(),
- anyInt(), anyInt(), anyInt(), anyBoolean(), anyString(), anyInt());
+ anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
}
@MediumTest
@@ -436,7 +437,7 @@
"5550000", PhoneNumberUtils.TOA_Unknown);
verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
verify(mMockBluetoothHeadset, times(2)).clccResponse(anyInt(),
- anyInt(), anyInt(), anyInt(), anyBoolean(), anyString(), anyInt());
+ anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
}
@MediumTest
@@ -490,7 +491,7 @@
"5550000", PhoneNumberUtils.TOA_Unknown);
verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
verify(mMockBluetoothHeadset, times(2)).clccResponse(anyInt(),
- anyInt(), anyInt(), anyInt(), anyBoolean(), anyString(), anyInt());
+ anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
}
@MediumTest
@@ -519,7 +520,7 @@
"5550001", PhoneNumberUtils.TOA_Unknown);
verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
verify(mMockBluetoothHeadset, times(3)).clccResponse(anyInt(),
- anyInt(), anyInt(), anyInt(), anyBoolean(), anyString(), anyInt());
+ anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
}
@MediumTest
@@ -607,7 +608,7 @@
boolean didProcess = mBluetoothPhoneService.mBinder.processChld(CHLD_TYPE_RELEASEHELD);
- verify(mMockCallsManager).rejectCall(eq(ringingCall), eq(false), any(String.class));
+ verify(mMockCallsManager).rejectCall(eq(ringingCall), eq(false), nullable(String.class));
assertEquals(didProcess, true);
}
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
index e6cb7bf..9b94f63 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
@@ -39,9 +39,8 @@
import java.util.List;
import java.util.Objects;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
@@ -228,7 +227,7 @@
BluetoothRouteManager.AUDIO_OFF_STATE_NAME, null);
setupConnectedDevices(new BluetoothDevice[]{device1}, null);
when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
- any(ContentResolver.class))).thenReturn(0L);
+ nullable(ContentResolver.class))).thenReturn(0L);
when(mHeadsetProxy.connectAudio()).thenReturn(false);
executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, null);
// Wait 3 times: for the first connection attempt, the retry attempt, and once more to
@@ -249,7 +248,7 @@
BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, device1);
setupConnectedDevices(new BluetoothDevice[]{device1, device2}, null);
when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
- any(ContentResolver.class))).thenReturn(0L);
+ nullable(ContentResolver.class))).thenReturn(0L);
when(mHeadsetProxy.connectAudio()).thenReturn(false);
executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, device2.getAddress());
// Wait 3 times: the first connection attempt is accounted for in executeRoutingAction,
@@ -426,7 +425,7 @@
BluetoothDevice first = getFirstExcluding(devices,
(String) invocation.getArguments()[0]);
return first == null ? null : first.getAddress();
- }).when(mDeviceManager).getMostRecentlyConnectedDevice(anyString());
+ }).when(mDeviceManager).getMostRecentlyConnectedDevice(nullable(String.class));
}
private void executeRoutingAction(BluetoothRouteManager brm, int message, String device) {
@@ -457,9 +456,9 @@
when(mDeviceManager.getHeadsetService()).thenReturn(mHeadsetProxy);
when(mHeadsetProxy.connectAudio()).thenReturn(true);
when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
- any(ContentResolver.class))).thenReturn(100000L);
+ nullable(ContentResolver.class))).thenReturn(100000L);
when(mTimeoutsAdapter.getBluetoothPendingTimeoutMillis(
- any(ContentResolver.class))).thenReturn(100000L);
+ nullable(ContentResolver.class))).thenReturn(100000L);
}
private static BluetoothDevice getFirstExcluding(
diff --git a/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java b/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
index 3767914..6782d52 100644
--- a/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
@@ -48,6 +48,7 @@
import java.util.Collections;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
@@ -110,11 +111,11 @@
when(mDefaultDialerCache.getDefaultDialerApplication(eq(UserHandle.USER_CURRENT)))
.thenReturn(PKG_NAME);
- when(mPackageManager.queryIntentServicesAsUser(any(Intent.class), anyInt(), anyInt()))
+ when(mPackageManager.queryIntentServicesAsUser(nullable(Intent.class), anyInt(), anyInt()))
.thenReturn(Collections.singletonList(mResolveInfo));
when(mParcelableCallUtilsConverter.toParcelableCall(
eq(mCall), anyBoolean(), eq(mPhoneAccountRegistrar))).thenReturn(null);
- when(mContext.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class),
+ when(mContext.bindServiceAsUser(nullable(Intent.class), nullable(ServiceConnection.class),
anyInt(), eq(UserHandle.CURRENT))).thenReturn(true);
}
@@ -128,7 +129,7 @@
@SmallTest
public void testNoResolveEntries() {
- when(mPackageManager.queryIntentServicesAsUser(any(Intent.class), anyInt(), anyInt()))
+ when(mPackageManager.queryIntentServicesAsUser(nullable(Intent.class), anyInt(), anyInt()))
.thenReturn(Collections.emptyList());
mFilter.startFilterLookup(mCall, mCallback);
verify(mCallback).onCallFilteringComplete(eq(mCall), eq(PASS_RESULT));
@@ -150,7 +151,7 @@
@SmallTest
public void testContextFailToBind() {
- when(mContext.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class),
+ when(mContext.bindServiceAsUser(nullable(Intent.class), nullable(ServiceConnection.class),
anyInt(), eq(UserHandle.CURRENT))).thenReturn(false);
mFilter.startFilterLookup(mCall, mCallback);
verify(mCallback).onCallFilteringComplete(eq(mCall), eq(PASS_RESULT));
@@ -159,7 +160,7 @@
@SmallTest
public void testExceptionInScreeningService() throws Exception {
doThrow(new RemoteException()).when(mCallScreeningService).screenCall(
- any(ICallScreeningAdapter.class), any(ParcelableCall.class));
+ nullable(ICallScreeningAdapter.class), nullable(ParcelableCall.class));
mFilter.startFilterLookup(mCall, mCallback);
ServiceConnection serviceConnection = verifyBindingIntent();
serviceConnection.onServiceConnected(COMPONENT_NAME, mBinder);
@@ -214,7 +215,7 @@
private ICallScreeningAdapter getCallScreeningAdapter() throws Exception {
ArgumentCaptor<ICallScreeningAdapter> captor =
ArgumentCaptor.forClass(ICallScreeningAdapter.class);
- verify(mCallScreeningService).screenCall(captor.capture(), any(ParcelableCall.class));
+ verify(mCallScreeningService).screenCall(captor.capture(), nullable(ParcelableCall.class));
return captor.getValue();
}
}
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index fa5b6f0..255af74 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -37,6 +37,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -209,6 +210,11 @@
}
@Override
+ public ApplicationInfo getApplicationInfo() {
+ return mTestApplicationInfo;
+ }
+
+ @Override
public ContentResolver getContentResolver() {
return new ContentResolver(mApplicationContextSpy) {
@Override
@@ -423,6 +429,7 @@
private final CountryDetector mCountryDetector = mock(CountryDetector.class);
private final Map<String, IContentProvider> mIContentProviderByUri = new HashMap<>();
private final Configuration mResourceConfiguration = new Configuration();
+ private final ApplicationInfo mTestApplicationInfo = new ApplicationInfo();
private TelecomManager mTelecomManager = null;
diff --git a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
index 0104172..350afa3 100644
--- a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
+++ b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
@@ -39,6 +39,7 @@
import java.util.ArrayList;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
@@ -242,7 +243,7 @@
// Put in a regular phone account to be sure it doesn't call that
PhoneAccountHandle pAHandle = getNewTargetPhoneAccountHandle("tel_acct");
when(mMockAccountRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
- any(String.class))).thenReturn(pAHandle);
+ nullable(String.class))).thenReturn(pAHandle);
// Include a normal Connection Manager to be sure it doesn't call that
PhoneAccount callManagerPA = getNewConnectionManagerPhoneAccount("cm_acct", 0);
// Include a connection Manager for the user with the capability to make calls
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index c44f92c..eb0c419 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -60,6 +60,7 @@
import java.util.Collections;
import java.util.LinkedList;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
@@ -406,11 +407,11 @@
when(mMockCall.isIncoming()).thenReturn(true);
when(mMockCall.isExternalCall()).thenReturn(false);
when(mDefaultDialerCache.getDefaultDialerApplication(CURRENT_USER_ID)).thenReturn(DEF_PKG);
- when(mMockContext.bindServiceAsUser(
- any(Intent.class), any(ServiceConnection.class), anyInt(), any(UserHandle.class)))
+ when(mMockContext.bindServiceAsUser(nullable(Intent.class),
+ nullable(ServiceConnection.class), anyInt(), nullable(UserHandle.class)))
.thenReturn(true);
- when(mTimeoutsAdapter.getCallRemoveUnbindInCallServicesDelay(any(ContentResolver.class)))
- .thenReturn(500L);
+ when(mTimeoutsAdapter.getCallRemoveUnbindInCallServicesDelay(
+ nullable(ContentResolver.class))).thenReturn(500L);
when(mMockCallsManager.getCalls()).thenReturn(Collections.singletonList(mMockCall));
setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
@@ -438,7 +439,7 @@
when(mockBinder.queryLocalInterface(anyString())).thenReturn(mockInCallService);
serviceConnection.onServiceConnected(defDialerComponentName, mockBinder);
- verify(mockInCallService).setInCallAdapter(any(IInCallAdapter.class));
+ verify(mockInCallService).setInCallAdapter(nullable(IInCallAdapter.class));
verify(mMockContext, never()).unbindService(serviceConnection);
verify(mockInCallService, never()).addCall(any(ParcelableCall.class));
diff --git a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
index 13a85af..0baad91 100644
--- a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
@@ -60,6 +60,7 @@
import java.util.LinkedList;
import java.util.List;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
@@ -207,9 +208,9 @@
ArgumentCaptor<Integer> requestIdCaptor = ArgumentCaptor.forClass(
Integer.class);
verify(mNotificationManager, times(2)).notifyAsUser(isNull(String.class),
- requestIdCaptor.capture(), any(Notification.class), eq(userHandle));
- verify(mNotificationManager).cancelAsUser(any(String.class), eq(requestIdCaptor.getValue()),
- eq(userHandle));
+ requestIdCaptor.capture(), nullable(Notification.class), eq(userHandle));
+ verify(mNotificationManager).cancelAsUser(nullable(String.class),
+ eq(requestIdCaptor.getValue()), eq(userHandle));
// Verify that the second call to showMissedCallNotification behaves like it were the first.
verify(builder2).setContentText(CALLER_NAME);
@@ -423,15 +424,15 @@
CallLog.Calls.PRESENTATION_ALLOWED, CALL_TIMESTAMP)
.build();
- when(cp.query(anyString(), eq(queryUri), any(String[].class), anyString(), any
- (String[].class), anyString(), any(ICancellationSignal.class)))
+ when(cp.query(anyString(), eq(queryUri), nullable(String[].class), nullable(String.class),
+ nullable(String[].class), nullable(String.class), any(ICancellationSignal.class)))
.thenReturn(mockMissedCallsCursor);
PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
MissedCallNotifier.CallInfo fakeCallInfo = makeFakeCallInfo(TEL_CALL_HANDLE,
CALLER_NAME, CALL_TIMESTAMP, phoneAccount.getAccountHandle());
- when(mockCallInfoFactory.makeCallInfo(any(CallerInfo.class),
- any(PhoneAccountHandle.class), any(Uri.class), eq(CALL_TIMESTAMP)))
+ when(mockCallInfoFactory.makeCallInfo(nullable(CallerInfo.class),
+ nullable(PhoneAccountHandle.class), nullable(Uri.class), eq(CALL_TIMESTAMP)))
.thenReturn(fakeCallInfo);
Notification.Builder builder1 = makeNotificationBuilder("builder1");
@@ -491,15 +492,15 @@
PRIMARY_USER.getIdentifier());
IContentProvider cp = getContentProviderForUser(PRIMARY_USER.getIdentifier());
- when(cp.query(anyString(), eq(queryUri), any(String[].class), anyString(), any
- (String[].class), anyString(), any(ICancellationSignal.class)))
+ when(cp.query(anyString(), eq(queryUri), nullable(String[].class), nullable(String.class),
+ nullable(String[].class), nullable(String.class), any(ICancellationSignal.class)))
.thenReturn(mockMissedCallsCursor);
PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
MissedCallNotifier.CallInfo fakeCallInfo = makeFakeCallInfo(TEL_CALL_HANDLE,
CALLER_NAME, CALL_TIMESTAMP, phoneAccount.getAccountHandle());
- when(mockCallInfoFactory.makeCallInfo(any(CallerInfo.class),
- any(PhoneAccountHandle.class), any(Uri.class), eq(CALL_TIMESTAMP)))
+ when(mockCallInfoFactory.makeCallInfo(nullable(CallerInfo.class),
+ nullable(PhoneAccountHandle.class), nullable(Uri.class), eq(CALL_TIMESTAMP)))
.thenReturn(fakeCallInfo);
Notification.Builder builder1 = makeNotificationBuilder("builder1");
@@ -534,7 +535,7 @@
// Verify that two notifications were generated, both with the same id.
verify(mNotificationManager, times(2)).notifyAsUser(isNull(String.class), eq(1),
- any(Notification.class), eq(PRIMARY_USER));
+ nullable(Notification.class), eq(PRIMARY_USER));
}
private Notification.Builder makeNotificationBuilder(String label) {
diff --git a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
index 9b7dc37..b23664d 100644
--- a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
+++ b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
@@ -43,6 +43,7 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
@@ -100,7 +101,7 @@
assertEquals(DisconnectCause.NOT_DISCONNECTED, result);
verify(mCallsManager).placeOutgoingCall(eq(mCall), eq(Uri.parse(voicemailNumber)),
- any(GatewayInfo.class), eq(true), eq(VideoProfile.STATE_AUDIO_ONLY));
+ nullable(GatewayInfo.class), eq(true), eq(VideoProfile.STATE_AUDIO_ONLY));
}
@SmallTest
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
new file mode 100644
index 0000000..8b269a9
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2017 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.app.NotificationManager;
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.Ringtone;
+import android.os.Bundle;
+import android.os.Vibrator;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.telecom.AsyncRingtonePlayer;
+import com.android.server.telecom.Call;
+import com.android.server.telecom.InCallController;
+import com.android.server.telecom.InCallTonePlayer;
+import com.android.server.telecom.Ringer;
+import com.android.server.telecom.RingtoneFactory;
+import com.android.server.telecom.SystemSettingsUtil;
+
+import org.mockito.Mock;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class RingerTest extends TelecomTestCase {
+ @Mock InCallTonePlayer.Factory mockPlayerFactory;
+ @Mock SystemSettingsUtil mockSystemSettingsUtil;
+ @Mock AsyncRingtonePlayer mockRingtonePlayer;
+ @Mock RingtoneFactory mockRingtoneFactory;
+ @Mock Vibrator mockVibrator;
+ @Mock InCallController mockInCallController;
+
+ @Mock InCallTonePlayer mockTonePlayer;
+ @Mock Call mockCall1;
+ @Mock Call mockCall2;
+
+ Ringer mRingerUnderTest;
+ AudioManager mockAudioManager;
+ public void setUp() throws Exception {
+ super.setUp();
+ mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
+ mRingerUnderTest = new Ringer(mockPlayerFactory, mContext, mockSystemSettingsUtil,
+ mockRingtonePlayer, mockRingtoneFactory, mockVibrator, mockInCallController);
+ when(mockPlayerFactory.createPlayer(anyInt())).thenReturn(mockTonePlayer);
+ mockAudioManager =
+ (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+ NotificationManager notificationManager =
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ when(notificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
+ }
+
+ @SmallTest
+ public void testNoActionInTheaterMode() {
+ // Start call waiting to make sure that it doesn't stop when we start ringing
+ mRingerUnderTest.startCallWaiting(mockCall1);
+ when(mockSystemSettingsUtil.isTheaterModeOn(any(Context.class))).thenReturn(true);
+ assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
+ verify(mockTonePlayer, never()).stopTone();
+ verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
+ verify(mockVibrator, never()).vibrate(
+ any(long[].class), anyInt(), any(AudioAttributes.class));
+ }
+
+ @SmallTest
+ public void testNoActionWhenDialerRings() {
+ // Start call waiting to make sure that it doesn't stop when we start ringing
+ mRingerUnderTest.startCallWaiting(mockCall1);
+ when(mockInCallController.doesConnectedDialerSupportRinging()).thenReturn(true);
+ assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
+ verify(mockTonePlayer, never()).stopTone();
+ verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
+ verify(mockVibrator, never()).vibrate(
+ any(long[].class), anyInt(), any(AudioAttributes.class));
+ }
+
+ @SmallTest
+ public void testAudioFocusStillAcquiredWhenDialerRings() {
+ // Start call waiting to make sure that it doesn't stop when we start ringing
+ mRingerUnderTest.startCallWaiting(mockCall1);
+ when(mockInCallController.doesConnectedDialerSupportRinging()).thenReturn(true);
+ ensureRingerIsAudible();
+ assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
+ verify(mockTonePlayer, never()).stopTone();
+ verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
+ verify(mockVibrator, never()).vibrate(
+ any(long[].class), anyInt(), any(AudioAttributes.class));
+ }
+
+ @SmallTest
+ public void testNoActionWhenCallIsSelfManaged() {
+ // Start call waiting to make sure that it doesn't stop when we start ringing
+ mRingerUnderTest.startCallWaiting(mockCall1);
+ when(mockCall2.isSelfManaged()).thenReturn(true);
+ // We do want to acquire audio focus when self-managed
+ assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
+ verify(mockTonePlayer, never()).stopTone();
+ verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
+ verify(mockVibrator, never()).vibrate(
+ any(long[].class), anyInt(), any(AudioAttributes.class));
+ }
+
+ @SmallTest
+ public void testCallWaitingButNoRingForSpecificContacts() {
+ NotificationManager notificationManager =
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ when(notificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(false);
+ // Start call waiting to make sure that it does stop when we start ringing
+ mRingerUnderTest.startCallWaiting(mockCall1);
+ verify(mockTonePlayer).startTone();
+
+ assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
+ verify(mockTonePlayer).stopTone();
+ verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
+ verify(mockVibrator, never()).vibrate(
+ any(long[].class), anyInt(), any(AudioAttributes.class));
+ }
+
+ @SmallTest
+ public void testVibrateButNoRingForNullRingtone() {
+ mRingerUnderTest.startCallWaiting(mockCall1);
+ when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(null);
+ when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+ enableVibrationWhenRinging();
+ assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
+ verify(mockTonePlayer).stopTone();
+ verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
+ verify(mockVibrator).vibrate(
+ any(long[].class), anyInt(), any(AudioAttributes.class));
+ }
+
+ @SmallTest
+ public void testVibrateButNoRingForSilentRingtone() {
+ mRingerUnderTest.startCallWaiting(mockCall1);
+ Ringtone mockRingtone = mock(Ringtone.class);
+ when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone);
+ when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+ when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
+ enableVibrationWhenRinging();
+ assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
+ verify(mockTonePlayer).stopTone();
+ verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
+ verify(mockVibrator).vibrate(
+ any(long[].class), anyInt(), any(AudioAttributes.class));
+ }
+
+ @SmallTest
+ public void testRingAndNoVibrate() {
+ mRingerUnderTest.startCallWaiting(mockCall1);
+ ensureRingerIsAudible();
+ enableVibrationOnlyWhenNotRinging();
+ assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
+ verify(mockTonePlayer).stopTone();
+ verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class));
+ verify(mockVibrator, never()).vibrate(
+ any(long[].class), anyInt(), any(AudioAttributes.class));
+ }
+
+ @SmallTest
+ public void testSilentRingWithHfpStillAcquiresFocus1() {
+ mRingerUnderTest.startCallWaiting(mockCall1);
+ Ringtone mockRingtone = mock(Ringtone.class);
+ when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone);
+ when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+ when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
+ enableVibrationOnlyWhenNotRinging();
+ assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
+ verify(mockTonePlayer).stopTone();
+ verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
+ verify(mockVibrator, never()).vibrate(
+ any(long[].class), anyInt(), any(AudioAttributes.class));
+ }
+
+ @SmallTest
+ public void testSilentRingWithHfpStillAcquiresFocus2() {
+ mRingerUnderTest.startCallWaiting(mockCall1);
+ when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(null);
+ when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+ when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
+ enableVibrationOnlyWhenNotRinging();
+ assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
+ verify(mockTonePlayer).stopTone();
+ verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
+ verify(mockVibrator, never()).vibrate(
+ any(long[].class), anyInt(), any(AudioAttributes.class));
+ }
+
+ private void ensureRingerIsAudible() {
+ Ringtone mockRingtone = mock(Ringtone.class);
+ when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone);
+ when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+ when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(100);
+ }
+
+ private void enableVibrationWhenRinging() {
+ when(mockVibrator.hasVibrator()).thenReturn(true);
+ when(mockSystemSettingsUtil.canVibrateWhenRinging(any(Context.class))).thenReturn(true);
+ }
+
+ private void enableVibrationOnlyWhenNotRinging() {
+ when(mockVibrator.hasVibrator()).thenReturn(true);
+ when(mockSystemSettingsUtil.canVibrateWhenRinging(any(Context.class))).thenReturn(false);
+ }
+}
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index 3d2a52e..bbfc11e 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -64,6 +64,7 @@
import static android.Manifest.permission.REGISTER_SIM_SUBSCRIPTION;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
@@ -253,7 +254,7 @@
@SmallTest
public void testSetUserSelectedOutgoingPhoneAccountFailure() throws RemoteException {
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
- anyString(), anyString());
+ anyString(), nullable(String.class));
try {
mTSIBinder.setUserSelectedOutgoingPhoneAccount(TEL_PA_HANDLE_16);
} catch (SecurityException e) {
@@ -276,12 +277,12 @@
}};
// Returns all phone accounts when getCallCapablePhoneAccounts is called.
when(mFakePhoneAccountRegistrar
- .getCallCapablePhoneAccounts(anyString(), eq(true), any(UserHandle.class)))
- .thenReturn(fullPHList);
+ .getCallCapablePhoneAccounts(nullable(String.class), eq(true),
+ nullable(UserHandle.class))).thenReturn(fullPHList);
// Returns only enabled phone accounts when getCallCapablePhoneAccounts is called.
when(mFakePhoneAccountRegistrar
- .getCallCapablePhoneAccounts(anyString(), eq(false), any(UserHandle.class)))
- .thenReturn(smallPHList);
+ .getCallCapablePhoneAccounts(nullable(String.class), eq(false),
+ nullable(UserHandle.class))).thenReturn(smallPHList);
makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_16, SIP_PA_HANDLE_17);
assertEquals(fullPHList,
@@ -428,7 +429,8 @@
.when(mContext).checkCallingOrSelfPermission(MODIFY_PHONE_STATE);
doThrow(new SecurityException())
.when(mContext)
- .enforceCallingOrSelfPermission(eq(REGISTER_SIM_SUBSCRIPTION), anyString());
+ .enforceCallingOrSelfPermission(eq(REGISTER_SIM_SUBSCRIPTION),
+ nullable(String.class));
registerPhoneAccountTestHelper(phoneAccount, false);
}
@@ -464,22 +466,10 @@
if (shouldSucceed) {
assertFalse(didExceptionOccur);
verify(mFakePhoneAccountRegistrar).registerPhoneAccount(testPhoneAccount);
- verify(mContext).sendBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL),
- anyString());
-
- Intent capturedIntent = intentCaptor.getValue();
- assertEquals(TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED,
- capturedIntent.getAction());
- Bundle intentExtras = capturedIntent.getExtras();
- assertEquals(1, intentExtras.size());
- assertEquals(testPhoneAccount.getAccountHandle(),
- intentExtras.get(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE));
} else {
assertTrue(didExceptionOccur);
verify(mFakePhoneAccountRegistrar, never())
.registerPhoneAccount(any(PhoneAccount.class));
- verify(mContext, never())
- .sendBroadcastAsUser(any(Intent.class), any(UserHandle.class), anyString());
}
}
@@ -495,14 +485,6 @@
mTSIBinder.unregisterPhoneAccount(phHandle);
verify(mFakePhoneAccountRegistrar).unregisterPhoneAccount(phHandle);
- verify(mContext).sendBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL),
- anyString());
- Intent capturedIntent = intentCaptor.getValue();
- assertEquals(TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED,
- capturedIntent.getAction());
- Bundle intentExtras = capturedIntent.getExtras();
- assertEquals(1, intentExtras.size());
- assertEquals(phHandle, intentExtras.get(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE));
}
@SmallTest
@@ -722,14 +704,14 @@
@SmallTest
public void testSetDefaultDialerNoModifyPhoneStatePermission() throws Exception {
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
- eq(MODIFY_PHONE_STATE), anyString());
+ eq(MODIFY_PHONE_STATE), nullable(String.class));
setDefaultDialerFailureTestHelper();
}
@SmallTest
public void testSetDefaultDialerNoWriteSecureSettingsPermission() throws Exception {
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
- eq(WRITE_SECURE_SETTINGS), anyString());
+ eq(WRITE_SECURE_SETTINGS), nullable(String.class));
setDefaultDialerFailureTestHelper();
}
@@ -854,21 +836,19 @@
@SmallTest
public void testAcceptRingingCall() throws Exception {
Call call = mock(Call.class);
- when(mFakeCallsManager.getFirstCallWithState(any(int[].class)))
- .thenReturn(call);
+ when(mFakeCallsManager.getFirstCallWithState(anyInt())).thenReturn(call);
// Not intended to be a real video state. Here to ensure that the call will be answered
// with whatever video state it's currently in.
int fakeVideoState = 29578215;
when(call.getVideoState()).thenReturn(fakeVideoState);
mTSIBinder.acceptRingingCall();
- verify(call).answer(fakeVideoState);
+ verify(call).answer(eq(fakeVideoState));
}
@SmallTest
public void testAcceptRingingCallWithValidVideoState() throws Exception {
Call call = mock(Call.class);
- when(mFakeCallsManager.getFirstCallWithState(any(int[].class)))
- .thenReturn(call);
+ when(mFakeCallsManager.getFirstCallWithState(anyInt())).thenReturn(call);
// Not intended to be a real video state. Here to ensure that the call will be answered
// with the video state passed in to acceptRingingCallWithVideoState
int fakeVideoState = 29578215;
@@ -878,6 +858,56 @@
verify(call).answer(realVideoState);
}
+ @SmallTest
+ public void testIsInCall() throws Exception {
+ when(mFakeCallsManager.hasOngoingCalls()).thenReturn(true);
+ assertTrue(mTSIBinder.isInCall(DEFAULT_DIALER_PACKAGE));
+ }
+
+ @SmallTest
+ public void testNotIsInCall() throws Exception {
+ when(mFakeCallsManager.hasOngoingCalls()).thenReturn(false);
+ assertFalse(mTSIBinder.isInCall(DEFAULT_DIALER_PACKAGE));
+ }
+
+ @SmallTest
+ public void testIsInCallFail() throws Exception {
+ doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+ anyString(), any());
+ try {
+ mTSIBinder.isInCall("blah");
+ fail();
+ } catch (SecurityException e) {
+ // desired result
+ }
+ verify(mFakeCallsManager, never()).hasOngoingCalls();
+ }
+
+ @SmallTest
+ public void testIsInManagedCall() throws Exception {
+ when(mFakeCallsManager.hasOngoingManagedCalls()).thenReturn(true);
+ assertTrue(mTSIBinder.isInManagedCall(DEFAULT_DIALER_PACKAGE));
+ }
+
+ @SmallTest
+ public void testNotIsInManagedCall() throws Exception {
+ when(mFakeCallsManager.hasOngoingManagedCalls()).thenReturn(false);
+ assertFalse(mTSIBinder.isInManagedCall(DEFAULT_DIALER_PACKAGE));
+ }
+
+ @SmallTest
+ public void testIsInManagedCallFail() throws Exception {
+ doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+ anyString(), any());
+ try {
+ mTSIBinder.isInManagedCall("blah");
+ fail();
+ } catch (SecurityException e) {
+ // desired result
+ }
+ verify(mFakeCallsManager, never()).hasOngoingCalls();
+ }
+
/**
* Register phone accounts for the supplied PhoneAccountHandles to make them
* visible to all users (via the isVisibleToCaller method in TelecomServiceImpl.
@@ -888,10 +918,10 @@
when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(eq(ph))).thenReturn(
makeMultiUserPhoneAccount(ph).build());
when(mFakePhoneAccountRegistrar
- .getPhoneAccount(eq(ph), any(UserHandle.class), anyBoolean()))
+ .getPhoneAccount(eq(ph), nullable(UserHandle.class), anyBoolean()))
.thenReturn(makeMultiUserPhoneAccount(ph).build());
when(mFakePhoneAccountRegistrar
- .getPhoneAccount(eq(ph), any(UserHandle.class)))
+ .getPhoneAccount(eq(ph), nullable(UserHandle.class)))
.thenReturn(makeMultiUserPhoneAccount(ph).build());
}
}
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 11e0dc5..1c81fec 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -17,6 +17,7 @@
package com.android.server.telecom.tests;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
@@ -24,6 +25,7 @@
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
@@ -310,6 +312,7 @@
super.setUp();
mSpyContext = mComponentContextFixture.getTestDouble().getApplicationContext();
doReturn(mSpyContext).when(mSpyContext).getApplicationContext();
+ doNothing().when(mSpyContext).sendBroadcastAsUser(any(), any(), any());
mNumOutgoingCallsMade = 0;
@@ -662,10 +665,10 @@
anyString(),
anyInt(),
newOutgoingCallReceiver.capture(),
- any(Handler.class),
+ nullable(Handler.class),
anyInt(),
anyString(),
- any(Bundle.class));
+ nullable(Bundle.class));
// Pass on the new outgoing call Intent
// Set a dummy PendingResult so the BroadcastReceiver agrees to accept onReceive()
diff --git a/tests/src/com/android/server/telecom/tests/VideoProviderTest.java b/tests/src/com/android/server/telecom/tests/VideoProviderTest.java
index f6734c7..3234c1f 100644
--- a/tests/src/com/android/server/telecom/tests/VideoProviderTest.java
+++ b/tests/src/com/android/server/telecom/tests/VideoProviderTest.java
@@ -173,6 +173,8 @@
doThrow(new SecurityException()).when(mContext)
.enforcePermission(anyString(), anyInt(), anyInt(), anyString());
+ // Set the target SDK version to to > N-MR1.
+ mVideoCallImpl.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT);
// Make a request to change the camera
mVideoCall.setCamera(MockVideoProvider.CAMERA_FRONT);
mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
@@ -199,6 +201,8 @@
doReturn(AppOpsManager.MODE_ERRORED).when(mAppOpsManager).noteOp(anyInt(), anyInt(),
anyString());
+ // Set the target SDK version to > N-MR1.
+ mVideoCallImpl.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT);
// Make a request to change the camera
mVideoCall.setCamera(MockVideoProvider.CAMERA_FRONT);
mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
@@ -213,6 +217,36 @@
}
/**
+ * Tests the caller app ops check in {@link VideoCall#setCamera(String)} to ensure a camera
+ * change from a non-permitted caller is ignored. For < N-MR1, throw a CAMERA_FAILURE instead
+ * of a CAMERA_PERMISSION_ERROR.
+ */
+ @MediumTest
+ public void testCameraChangeAppOpsBelowNMR1Fail() throws Exception {
+ // Wait until the callback has been received before performing verification.
+ doAnswer(mVerification).when(mVideoCallCallback).onCallSessionEvent(anyInt());
+
+ // ensure app ops check fails.
+ doReturn(AppOpsManager.MODE_ERRORED).when(mAppOpsManager).noteOp(anyInt(), anyInt(),
+ anyString());
+
+ // Set the target SDK version to below N-MR1
+ mVideoCallImpl.setTargetSdkVersion(Build.VERSION_CODES.N);
+
+ // Make a request to change the camera
+ mVideoCall.setCamera(MockVideoProvider.CAMERA_FRONT);
+ mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
+
+ // Capture the session event reported via the callback.
+ ArgumentCaptor<Integer> sessionEventCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mVideoCallCallback, timeout(TEST_TIMEOUT)).onCallSessionEvent(
+ sessionEventCaptor.capture());
+
+ assertEquals(VideoProvider.SESSION_EVENT_CAMERA_FAILURE,
+ sessionEventCaptor.getValue().intValue());
+ }
+
+ /**
* Tests the caller user handle check in {@link VideoCall#setCamera(String)} to ensure a camera
* change from a background user is not permitted.
*/
@@ -224,6 +258,8 @@
// Set a fake user to be the current foreground user.
mTelecomSystem.getCallsManager().onUserSwitch(new UserHandle(1000));
+ // Set the target SDK version to > N-MR1
+ mVideoCallImpl.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT);
// Make a request to change the camera
mVideoCall.setCamera(MockVideoProvider.CAMERA_FRONT);
mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);