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);