Merge "Override getDeviceId() of mock context"
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index cd50d24..64645a4 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -59,7 +59,7 @@
     <string name="change_default_dialer_warning_message" msgid="8461963987376916114">"Deien aspektu guztiak erabili eta kontrolatu ahal izango ditu <xliff:g id="NEW_APP">%s</xliff:g> aplikazioak. Fidagarriak diren aplikazioak bakarrik erabili beharko lirateke Telefonoa aplikazio lehenetsi gisa."</string>
     <string name="change_default_call_screening_dialog_title" msgid="5365787219927262408">"Deiak iragazteko aplikazio lehenetsia <xliff:g id="NEW_APP">%s</xliff:g> izatea nahi duzu?"</string>
     <string name="change_default_call_screening_warning_message_for_disable_old_app" msgid="2039830033533243164">"Aurrerantzean, <xliff:g id="OLD_APP">%s</xliff:g> aplikazioak ezingo ditu iragazi deiak."</string>
-    <string name="change_default_call_screening_warning_message" msgid="9020537562292754269">"Kontaktuetan gordeta ez dituzun deitzaileei buruzko informazioa ikusteko eta dei horiek blokeatzeko gai izango da <xliff:g id="NEW_APP">%s</xliff:g>. Aplikazio fidagarriak soilik ezarri beharko lirateke deiak iragazteko aplikazio lehenetsi gisa."</string>
+    <string name="change_default_call_screening_warning_message" msgid="9020537562292754269">"Kontaktuetan gordeta ez dauzkazun deitzaileei buruzko informazioa ikusteko eta dei horiek blokeatzeko gai izango da <xliff:g id="NEW_APP">%s</xliff:g>. Aplikazio fidagarriak soilik ezarri beharko lirateke deiak iragazteko aplikazio lehenetsi gisa."</string>
     <string name="change_default_call_screening_dialog_affirmative" msgid="7162433828280058647">"Ezarri lehenetsi gisa"</string>
     <string name="change_default_call_screening_dialog_negative" msgid="1839266125623106342">"Utzi"</string>
     <string name="blocked_numbers" msgid="8322134197039865180">"Blokeatutako zenbakiak"</string>
diff --git a/src/com/android/server/telecom/CallAnomalyWatchdog.java b/src/com/android/server/telecom/CallAnomalyWatchdog.java
index b1c69e6..d3dbbc9 100644
--- a/src/com/android/server/telecom/CallAnomalyWatchdog.java
+++ b/src/com/android/server/telecom/CallAnomalyWatchdog.java
@@ -32,6 +32,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
@@ -110,6 +111,7 @@
     private final TelecomSystem.SyncRoot mLock;
     private final Timeouts.Adapter mTimeoutAdapter;
     private final ClockProxy mClockProxy;
+    private AnomalyReporterAdapter mAnomalyReporter = new AnomalyReporterAdapterImpl();
     // Pre-allocate space for 2 calls; realistically thats all we should ever need (tm)
     private final Map<Call, ScheduledFuture<?>> mScheduledFutureMap = new ConcurrentHashMap<>(2);
     private final Map<Call, WatchdogCallState> mWatchdogCallStateMap = new ConcurrentHashMap<>(2);
@@ -125,6 +127,22 @@
      */
     private static final String ENABLE_DISCONNECT_CALL_ON_STUCK_STATE =
             "enable_disconnect_call_on_stuck_state";
+    /**
+     * Anomaly Report UUIDs and corresponding event descriptions specific to CallAnomalyWatchdog.
+     */
+    public static final UUID WATCHDOG_DISCONNECTED_STUCK_CALL_UUID =
+            UUID.fromString("4b093985-c78f-45e3-a9fe-5319f397b025");
+    public static final String WATCHDOG_DISCONNECTED_STUCK_CALL_MSG =
+            "Telecom CallAnomalyWatchdog caught and disconnected a stuck/zombie call.";
+    public static final UUID WATCHDOG_DISCONNECTED_STUCK_EMERGENCY_CALL_UUID =
+            UUID.fromString("d57d8aab-d723-485e-a0dd-d1abb0f346c8");
+    public static final String WATCHDOG_DISCONNECTED_STUCK_EMERGENCY_CALL_MSG =
+            "Telecom CallAnomalyWatchdog caught and disconnected a stuck/zombie emergency call.";
+
+    @VisibleForTesting
+    public void setAnomalyReporterAdapter(AnomalyReporterAdapter mAnomalyReporterAdapter){
+        mAnomalyReporter = mAnomalyReporterAdapter;
+    }
 
     public CallAnomalyWatchdog(ScheduledExecutorService executorService,
             TelecomSystem.SyncRoot lock,
@@ -306,6 +324,15 @@
                     Log.addEvent(call, STATE_TIMEOUT, newState);
                     mLocalLog.log("STATE_TIMEOUT; callId=" + call.getId() + " in state "
                             + newState);
+                    if (call.isEmergencyCall()){
+                        mAnomalyReporter.reportAnomaly(
+                                WATCHDOG_DISCONNECTED_STUCK_EMERGENCY_CALL_UUID,
+                                WATCHDOG_DISCONNECTED_STUCK_EMERGENCY_CALL_MSG);
+                    } else {
+                        mAnomalyReporter.reportAnomaly(
+                                WATCHDOG_DISCONNECTED_STUCK_CALL_UUID,
+                                WATCHDOG_DISCONNECTED_STUCK_CALL_MSG);
+                    }
 
                     if (isEnabledDisconnect) {
                         call.setOverrideDisconnectCauseCode(
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 9960a44..8cac314 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -486,19 +486,20 @@
                 CallAudioRouteStateMachine.INCLUDE_BLUETOOTH_IN_BASELINE);
     }
 
-    Set<UserHandle> silenceRingers(Context context, UserHandle callingUser) {
+    Set<UserHandle> silenceRingers(Context context, UserHandle callingUser,
+            boolean hasCrossUserPermission) {
         // Store all users from calls that were silenced so that we can silence the
         // InCallServices which are associated with those users.
         Set<UserHandle> userHandles = new HashSet<>();
         boolean allCallSilenced = true;
         synchronized (mCallsManager.getLock()) {
             for (Call call : mRingingCalls) {
-                PhoneAccount targetPhoneAccount = call.getPhoneAccountFromHandle();
                 UserHandle userFromCall = call.getUserHandleFromTargetPhoneAccount();
                 // Do not try to silence calls when calling user is different from the phone account
-                // user and the account does not have CAPABILITY_MULTI_USER enabled.
-                if (!callingUser.equals(userFromCall) && !targetPhoneAccount.
-                        hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
+                // user, the account does not have CAPABILITY_MULTI_USER enabled, or if the user
+                // does not have the INTERACT_ACROSS_USERS permission enabled.
+                if (!hasCrossUserPermission && !mCallsManager
+                        .isCallVisibleForUser(call, callingUser)) {
                     allCallSilenced = false;
                     continue;
                 }
diff --git a/src/com/android/server/telecom/CallStreamingController.java b/src/com/android/server/telecom/CallStreamingController.java
index 912d47a..56ce17b 100644
--- a/src/com/android/server/telecom/CallStreamingController.java
+++ b/src/com/android/server/telecom/CallStreamingController.java
@@ -18,6 +18,7 @@
 
 import static android.telecom.CallStreamingService.STREAMING_FAILED_SENDER_BINDING_ERROR;
 
+import android.Manifest;
 import android.annotation.SuppressLint;
 import android.app.role.RoleManager;
 import android.content.ComponentName;
@@ -206,6 +207,15 @@
             }
 
             ServiceInfo serviceInfo = infos.get(0).serviceInfo;
+
+            if (serviceInfo.permission == null || !serviceInfo.permission.equals(
+                    Manifest.permission.BIND_CALL_STREAMING_SERVICE)) {
+                android.telecom.Log.w(TAG, "Must require BIND_CALL_STREAMING_SERVICE: " +
+                        serviceInfo.packageName);
+                future.complete(new VoipCallTransactionResult(
+                        VoipCallTransactionResult.RESULT_FAILED, MESSAGE));
+                return future;
+            }
             Intent intent = new Intent(CallStreamingService.SERVICE_INTERFACE);
             intent.setComponent(serviceInfo.getComponentName());
 
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index ed83880..48aa9e6 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -61,6 +61,7 @@
 import android.media.ToneGenerator;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -107,6 +108,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.Button;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -611,7 +613,8 @@
         mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer,
                 ringtoneFactory, systemVibrator,
                 new Ringer.VibrationEffectProxy(), mInCallController,
-                mContext.getSystemService(NotificationManager.class));
+                mContext.getSystemService(NotificationManager.class),
+                mContext.getSystemService(AccessibilityManager.class));
         mCallRecordingTonePlayer = new CallRecordingTonePlayer(mContext, audioManager,
                 mTimeoutsAdapter, mLock);
         mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
@@ -3676,10 +3679,6 @@
         return false;
     }
 
-    boolean hasActiveOrHoldingCall() {
-        return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null;
-    }
-
     boolean hasRingingCall() {
         return getFirstCallWithState(CallState.RINGING, CallState.ANSWERED) != null;
     }
@@ -3801,15 +3800,6 @@
         return getFirstCallWithState(CallState.ACTIVE);
     }
 
-    Call getDialingCall() {
-        return getFirstCallWithState(CallState.DIALING);
-    }
-
-    @VisibleForTesting
-    public Call getHeldCall() {
-        return getFirstCallWithState(CallState.ON_HOLD);
-    }
-
     public Call getHeldCallByConnectionService(PhoneAccountHandle targetPhoneAccount) {
         Optional<Call> heldCall = mCalls.stream()
                 .filter(call -> PhoneAccountHandle.areFromSamePackage(call.getTargetPhoneAccount(),
@@ -4422,6 +4412,60 @@
         return (int) callsStream.count();
     }
 
+    /**
+     * Determines the number of calls (visible to the calling user) matching the specified criteria.
+     * This is an overloaded method which is being used in a security patch to fix up the call
+     * state type APIs which are acting across users when they should not be.
+     *
+     * See {@link TelecomManager#isInCall()} and {@link TelecomManager#isInManagedCall()}.
+     *
+     * @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 callingUser Where {@code non-null}, call visibility is scoped to this
+     *                    {@link UserHandle}.
+     * @param hasCrossUserAccess indicates if calling user has the INTERACT_ACROSS_USERS permission.
+     * @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,
+            UserHandle callingUser, boolean hasCrossUserAccess,
+            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());
+
+        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);
+        }
+
+        // If a phone account handle was specified, only consider calls for that phone account.
+        if (phoneAccountHandle != null) {
+            callsStream = callsStream.filter(
+                    call -> phoneAccountHandle.equals(call.getTargetPhoneAccount()));
+        }
+
+        callsStream = callsStream.filter(
+                call -> hasCrossUserAccess || isCallVisibleForUser(call, callingUser));
+
+        return (int) callsStream.count();
+    }
+
     private boolean hasMaximumLiveCalls(Call exceptCall) {
         return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(CALL_FILTER_ALL,
                 exceptCall, null /* phoneAccountHandle*/, LIVE_CALL_STATES);
@@ -4516,23 +4560,29 @@
     /**
      * Determines if there are any ongoing managed or self-managed calls.
      * Note: The {@link #ONGOING_CALL_STATES} are
+     * @param callingUser The user to scope the calls to.
+     * @param hasCrossUserAccess indicates if user has the INTERACT_ACROSS_USERS permission.
      * @return {@code true} if there are ongoing managed or self-managed calls, {@code false}
      *      otherwise.
      */
-    public boolean hasOngoingCalls() {
+    public boolean hasOngoingCalls(UserHandle callingUser, boolean hasCrossUserAccess) {
         return getNumCallsWithState(
                 CALL_FILTER_ALL, null /* excludeCall */,
+                callingUser, hasCrossUserAccess,
                 null /* phoneAccountHandle */,
                 ONGOING_CALL_STATES) > 0;
     }
 
     /**
      * Determines if there are any ongoing managed calls.
+     * @param callingUser The user to scope the calls to.
+     * @param hasCrossUserAccess indicates if user has the INTERACT_ACROSS_USERS permission.
      * @return {@code true} if there are ongoing managed calls, {@code false} otherwise.
      */
-    public boolean hasOngoingManagedCalls() {
+    public boolean hasOngoingManagedCalls(UserHandle callingUser, boolean hasCrossUserAccess) {
         return getNumCallsWithState(
                 CALL_FILTER_MANAGED, null /* excludeCall */,
+                callingUser, hasCrossUserAccess,
                 null /* phoneAccountHandle */,
                 ONGOING_CALL_STATES) > 0;
     }
@@ -6013,6 +6063,19 @@
     }
 
     /**
+     * Determines if a {@link Call} is visible to the calling user. If the {@link PhoneAccount} has
+     * CAPABILITY_MULTI_USER, or the user handle associated with the {@link PhoneAccount} is the
+     * same as the calling user, the call is visible to the user.
+     * @param call
+     * @return {@code true} if call is visible to the calling user
+     */
+    boolean isCallVisibleForUser(Call call, UserHandle userHandle) {
+        return call.getUserHandleFromTargetPhoneAccount().equals(userHandle)
+                || call.getPhoneAccountFromHandle()
+                .hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER);
+    }
+
+    /**
      * Determines if two {@link Call} instances originated from either the same target
      * {@link PhoneAccountHandle} or connection manager {@link PhoneAccountHandle}.
      * @param call1 The first call
diff --git a/src/com/android/server/telecom/LogUtils.java b/src/com/android/server/telecom/LogUtils.java
index 0481de1..35f8d1e 100644
--- a/src/com/android/server/telecom/LogUtils.java
+++ b/src/com/android/server/telecom/LogUtils.java
@@ -214,6 +214,8 @@
         public static final String SET_VOIP_MODE = "SET_VOIP_MODE";
         public static final String STATE_TIMEOUT = "STATE_TIMEOUT";
         public static final String ICS_EXTRAS_CHANGED = "ICS_EXTRAS_CHANGED";
+        public static final String FLASH_NOTIFICATION_START = "FLASH_NOTIFICATION_START";
+        public static final String FLASH_NOTIFICATION_STOP = "FLASH_NOTIFICATION_STOP";
 
         public static class Timings {
             public static final String ACCEPT_TIMING = "accept";
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 2828d12..171745f 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -37,6 +37,7 @@
 import android.os.Vibrator;
 import android.telecom.Log;
 import android.telecom.TelecomManager;
+import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.telecom.LogUtils.EventTimer;
@@ -161,6 +162,7 @@
     private RingtoneFactory mRingtoneFactory;
     private AudioManager mAudioManager;
     private NotificationManager mNotificationManager;
+    private AccessibilityManager mAccessibilityManager;
 
     /**
      * Call objects that are ringing, vibrating or call-waiting. These are used only for logging
@@ -194,7 +196,8 @@
             Vibrator vibrator,
             VibrationEffectProxy vibrationEffectProxy,
             InCallController inCallController,
-            NotificationManager notificationManager) {
+            NotificationManager notificationManager,
+            AccessibilityManager accessibilityManager) {
 
         mLock = new Object();
         mSystemSettingsUtil = systemSettingsUtil;
@@ -208,6 +211,7 @@
         mInCallController = inCallController;
         mVibrationEffectProxy = vibrationEffectProxy;
         mNotificationManager = notificationManager;
+        mAccessibilityManager = accessibilityManager;
 
         if (mContext.getResources().getBoolean(R.bool.use_simple_vibration_pattern)) {
             mDefaultVibrationEffect = mVibrationEffectProxy.createWaveform(SIMPLE_VIBRATION_PATTERN,
@@ -261,7 +265,7 @@
             attributes = ringerAttributesFuture.get(
                     RINGER_ATTRIBUTES_TIMEOUT, TimeUnit.MILLISECONDS);
         } catch (ExecutionException | InterruptedException | TimeoutException e) {
-            // Keep attributs as null
+            // Keep attributes as null
             Log.i(this, "getAttributes error: " + e);
         }
 
@@ -290,6 +294,13 @@
 
         stopCallWaiting();
 
+        final boolean shouldFlash = attributes.shouldRingForContact();
+        if (mAccessibilityManager != null && shouldFlash) {
+            Log.addEvent(foregroundCall, LogUtils.Events.FLASH_NOTIFICATION_START);
+            getHandler().post(() -> mAccessibilityManager.startFlashNotificationSequence(mContext,
+                    AccessibilityManager.FLASH_REASON_CALL));
+        }
+
         // Determine if the settings and DND mode indicate that the vibrator can be used right now.
         final boolean isVibratorEnabled =
             isVibratorEnabled(mContext, attributes.shouldRingForContact());
@@ -427,6 +438,7 @@
                     "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b",
                     mVibrator.hasVibrator(), mSystemSettingsUtil.isRingVibrationEnabled(mContext),
                     mAudioManager.getRingerMode(), mIsVibrating);
+                mVibratingCall = foregroundCall;
                 mIsVibrating = true;
                 mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
                 Log.i(this, "start vibration.");
@@ -499,6 +511,12 @@
     }
 
     public void stopRinging() {
+        final Call foregroundCall = mRingingCall != null ? mRingingCall : mVibratingCall;
+        if (mAccessibilityManager != null) {
+            Log.addEvent(foregroundCall, LogUtils.Events.FLASH_NOTIFICATION_STOP);
+            getHandler().post(() -> mAccessibilityManager.stopFlashNotificationSequence(mContext));
+        }
+
         synchronized (mLock) {
             if (mRingingCall != null) {
                 Log.addEvent(mRingingCall, LogUtils.Events.STOP_RINGER);
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 57987de..6826290 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -362,15 +362,12 @@
                 }
                 synchronized (mLock) {
                     final UserHandle callingUserHandle = Binder.getCallingUserHandle();
-                    boolean hasCrossUserAccess = mContext.checkCallingOrSelfPermission(
-                            Manifest.permission.INTERACT_ACROSS_USERS)
-                            == PackageManager.PERMISSION_GRANTED;
                     long token = Binder.clearCallingIdentity();
                     try {
                         return new ParceledListSlice<>(
                                 mPhoneAccountRegistrar.getCallCapablePhoneAccounts(null,
                                         includeDisabledAccounts, callingUserHandle,
-                                        hasCrossUserAccess));
+                                        hasInAppCrossUserPermission()));
                     } catch (Exception e) {
                         Log.e(this, e, "getCallCapablePhoneAccounts");
                         mAnomalyReporter.reportAnomaly(GET_CALL_CAPABLE_ACCOUNTS_ERROR_UUID,
@@ -640,13 +637,11 @@
 
                 synchronized (mLock) {
                     final UserHandle callingUserHandle = Binder.getCallingUserHandle();
-                    boolean hasCrossUserAccess = mContext.checkCallingOrSelfPermission(
-                            Manifest.permission.INTERACT_ACROSS_USERS)
-                            == PackageManager.PERMISSION_GRANTED;
                     long token = Binder.clearCallingIdentity();
                     try {
                         return new ParceledListSlice<>(mPhoneAccountRegistrar
-                                .getAllPhoneAccountHandles(callingUserHandle, hasCrossUserAccess));
+                                .getAllPhoneAccountHandles(callingUserHandle,
+                                        hasInAppCrossUserPermission()));
                     } catch (Exception e) {
                         Log.e(this, e, "getAllPhoneAccounts");
                         throw e;
@@ -954,7 +949,8 @@
                     try {
                         Log.i(this, "Silence Ringer requested by %s", callingPackage);
                         Set<UserHandle> userHandles = mCallsManager.getCallAudioManager().
-                                silenceRingers(mContext, callingUserHandle);
+                                silenceRingers(mContext, callingUserHandle,
+                                        hasInAppCrossUserPermission());
                         mCallsManager.getInCallController().silenceRinger(userHandles);
                     } finally {
                         Binder.restoreCallingIdentity(token);
@@ -1071,7 +1067,8 @@
                 }
 
                 synchronized (mLock) {
-                    return mCallsManager.hasOngoingCalls();
+                    return mCallsManager.hasOngoingCalls(Binder.getCallingUserHandle(),
+                            hasInAppCrossUserPermission());
                 }
             } finally {
                 Log.endSession();
@@ -1112,7 +1109,8 @@
                 }
 
                 synchronized (mLock) {
-                    return mCallsManager.hasOngoingManagedCalls();
+                    return mCallsManager.hasOngoingManagedCalls(Binder.getCallingUserHandle(),
+                            hasInAppCrossUserPermission());
                 }
             } finally {
                 Log.endSession();
@@ -2802,6 +2800,12 @@
                         + " INTERACT_ACROSS_USERS permission");
     }
 
+    private boolean hasInAppCrossUserPermission() {
+        return mContext.checkCallingOrSelfPermission(
+                Manifest.permission.INTERACT_ACROSS_USERS)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
     // to be used for TestApi methods that can only be called with SHELL UID.
     private void enforceShellOnly(int callingUid, String message) {
         if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index 2b5a80b..473e7b9 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -650,7 +650,12 @@
         BluetoothDevice activeDevice = mBluetoothRouteManager.getBluetoothAudioConnectedDevice();
         Log.i(this, "isInbandRingingEnabled: activeDevice: " + activeDevice);
         if (mBluetoothRouteManager.isCachedLeAudioDevice(activeDevice)) {
-            return true;
+            if (mBluetoothLeAudioService == null) {
+                Log.i(this, "isInbandRingingEnabled: no leaudio service available.");
+                return false;
+            }
+            int groupId = mBluetoothLeAudioService.getGroupId(activeDevice);
+            return mBluetoothLeAudioService.isInbandRingtoneEnabled(groupId);
         } else {
             if (mBluetoothHeadset == null) {
                 Log.i(this, "isInbandRingingEnabled: no headset service available.");
diff --git a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
index 95493e9..cdf2efa 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
@@ -16,8 +16,7 @@
 
 package com.android.server.telecom.transactionalVoipApp;
 
-
-import android.telecom.CallAudioState;
+import android.telecom.CallControlCallback;
 import android.telecom.CallEndpoint;
 import android.telecom.CallControl;
 import android.telecom.CallEventCallback;
@@ -28,7 +27,7 @@
 
 import java.util.function.Consumer;
 
-public class MyVoipCall implements CallEventCallback {
+public class MyVoipCall implements CallControlCallback, CallEventCallback {
 
     private static final String TAG = "MyVoipCall";
     private final String mCallId;
@@ -99,6 +98,5 @@
 
     @Override
     public void onMuteStateChanged(boolean isMuted) {
-
     }
 }
diff --git a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/VoipAppMainActivity.java b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/VoipAppMainActivity.java
index 775947b..4e1ec4c 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/VoipAppMainActivity.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/VoipAppMainActivity.java
@@ -135,7 +135,7 @@
 
                     }
                 },
-                call);
+                call, call);
     }
 
     private void setCallActive(MyVoipCall call) {
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
index 799f989..c37d136 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
@@ -522,6 +522,8 @@
     @Test
     public void testInBandRingingEnabledForLeDevice() {
         when(mBluetoothHeadset.isInbandRingingEnabled()).thenReturn(false);
+        when(mBluetoothLeAudio.isInbandRingtoneEnabled(1)).thenReturn(true);
+        when(mBluetoothLeAudio.getGroupId(eq(device3))).thenReturn(1);
         receiverUnderTest.onReceive(mContext,
                 buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1,
                         BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
diff --git a/tests/src/com/android/server/telecom/tests/CallAnomalyWatchdogTest.java b/tests/src/com/android/server/telecom/tests/CallAnomalyWatchdogTest.java
index efbebf1..93d1314 100644
--- a/tests/src/com/android/server/telecom/tests/CallAnomalyWatchdogTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAnomalyWatchdogTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
@@ -28,6 +29,7 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 
+import com.android.server.telecom.AnomalyReporterAdapter;
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallAnomalyWatchdog;
 import com.android.server.telecom.CallState;
@@ -81,6 +83,7 @@
     @Mock private ToastFactory mMockToastProxy;
     @Mock private PhoneNumberUtilsAdapter mMockPhoneNumberUtilsAdapter;
     @Mock private ConnectionServiceWrapper mMockConnectionService;
+    @Mock private AnomalyReporterAdapter mAnomalyReporterAdapter;
 
     @Override
     @Before
@@ -114,6 +117,7 @@
                 .when(mMockConnectionService).getComponentName();
         mCallAnomalyWatchdog = new CallAnomalyWatchdog(mTestScheduledExecutorService, mLock,
                 mTimeouts, mMockClockProxy);
+        mCallAnomalyWatchdog.setAnomalyReporterAdapter(mAnomalyReporterAdapter);
     }
 
     @Override
@@ -123,6 +127,21 @@
     }
 
     /**
+     * Helper function that setups the call being tested.
+     */
+    private Call setupCallHelper(int callState, boolean isCreateConnectionComplete,
+            ConnectionServiceWrapper service, boolean isVoipAudioMode, boolean isEmergencyCall) {
+        Call call = getCall();
+        call.setState(callState, "foo");
+        call.setIsCreateConnectionComplete(isCreateConnectionComplete);
+        if (service != null) call.setConnectionService(service);
+        call.setIsVoipAudioMode(isVoipAudioMode);
+        call.setIsEmergencyCall(isEmergencyCall);
+        mCallAnomalyWatchdog.onCallAdded(call);
+        return call;
+    }
+
+    /**
      * Test that the anomaly call state class correctly reports whether the state is transitory or
      * not for the purposes of the call anomaly watchdog.
      */
@@ -269,12 +288,7 @@
      */
      @Test
     public void testAddVoipRingingCall() {
-        Call call = getCall();
-        call.setState(CallState.RINGING, "foo");
-        call.setIsCreateConnectionComplete(false);
-        call.setIsVoipAudioMode(true);
-        call.setIsEmergencyCall(false);
-        mCallAnomalyWatchdog.onCallCreated(call);
+        Call call = setupCallHelper(CallState.RINGING, false, null, true, false);
 
         // Newly created call which hasn't been added; should schedule timeout.
         assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -308,12 +322,7 @@
      */
     @Test
     public void testAddVoipEmergencyRingingCall() {
-        Call call = getCall();
-        call.setState(CallState.RINGING, "foo");
-        call.setIsCreateConnectionComplete(false);
-        call.setIsVoipAudioMode(true);
-        call.setIsEmergencyCall(true);
-        mCallAnomalyWatchdog.onCallCreated(call);
+        Call call = setupCallHelper(CallState.RINGING, false, null, true, true);
 
         // Newly created call which hasn't been added; should schedule timeout.
         assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -348,12 +357,7 @@
      */
     @Test
     public void testAddNonVoipRingingCall() {
-        Call call = getCall();
-        call.setState(CallState.RINGING, "foo");
-        call.setIsCreateConnectionComplete(false);
-        call.setIsVoipAudioMode(false);
-        call.setIsEmergencyCall(false);
-        mCallAnomalyWatchdog.onCallCreated(call);
+        Call call = setupCallHelper(CallState.RINGING, false, null, false, false);
 
         // Newly created call which hasn't been added; should schedule timeout.
         assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -388,12 +392,7 @@
      */
     @Test
     public void testAddNonVoipEmergencyRingingCall() {
-        Call call = getCall();
-        call.setState(CallState.RINGING, "foo");
-        call.setIsCreateConnectionComplete(false);
-        call.setIsVoipAudioMode(false);
-        call.setIsEmergencyCall(true);
-        mCallAnomalyWatchdog.onCallCreated(call);
+        Call call = setupCallHelper(CallState.RINGING, false, null, false, true);
 
         // Newly created call which hasn't been added; should schedule timeout.
         assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -426,12 +425,7 @@
      */
     @Test
     public void testAddVoipRingingCallTimeoutWithoutConnection() {
-        Call call = getCall();
-        call.setState(CallState.RINGING, "foo");
-        call.setIsCreateConnectionComplete(false);
-        call.setIsVoipAudioMode(true);
-        call.setIsEmergencyCall(false);
-        mCallAnomalyWatchdog.onCallCreated(call);
+        setupCallHelper(CallState.RINGING, false, null, true, false);
 
         // Newly created call which hasn't been added; should schedule timeout.
         assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -454,12 +448,7 @@
      */
     @Test
     public void testAddVoipEmergencyRingingCallTimeoutWithoutConnection() {
-        Call call = getCall();
-        call.setState(CallState.RINGING, "foo");
-        call.setIsCreateConnectionComplete(false);
-        call.setIsVoipAudioMode(true);
-        call.setIsEmergencyCall(true);
-        mCallAnomalyWatchdog.onCallCreated(call);
+        setupCallHelper(CallState.RINGING, false, null, true, true);
 
         // Newly created call which hasn't been added; should schedule timeout.
         assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -483,12 +472,7 @@
      */
     @Test
     public void testAddNonVoipRingingCallTimeoutWithoutConnection() {
-        Call call = getCall();
-        call.setState(CallState.RINGING, "foo");
-        call.setIsCreateConnectionComplete(false);
-        call.setIsVoipAudioMode(false);
-        call.setIsEmergencyCall(false);
-        mCallAnomalyWatchdog.onCallCreated(call);
+        setupCallHelper(CallState.RINGING, false, null, false, false);;
 
         // Newly created call which hasn't been added; should schedule timeout.
         assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -511,12 +495,7 @@
      */
     @Test
     public void testAddNonVoipEmergencyRingingCallTimeoutWithoutConnection() {
-        Call call = getCall();
-        call.setState(CallState.RINGING, "foo");
-        call.setIsCreateConnectionComplete(false);
-        call.setIsVoipAudioMode(false);
-        call.setIsEmergencyCall(true);
-        mCallAnomalyWatchdog.onCallCreated(call);
+        setupCallHelper(CallState.RINGING, false, null, false, true);
 
         // Newly created call which hasn't been added; should schedule timeout.
         assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -540,13 +519,7 @@
      */
     @Test
     public void testAddVoipRingingCallTimeoutWithConnection() {
-        Call call = getCall();
-        call.setState(CallState.RINGING, "foo");
-        call.setIsCreateConnectionComplete(true);
-        call.setConnectionService(mMockConnectionService);
-        call.setIsVoipAudioMode(true);
-        call.setIsEmergencyCall(false);
-        mCallAnomalyWatchdog.onCallAdded(call);
+        setupCallHelper(CallState.RINGING, true, mMockConnectionService, true, false);
 
         // Newly created call which hasn't been added; should schedule timeout.
         assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -569,13 +542,7 @@
      */
     @Test
     public void testAddVoipEmergencyRingingCallTimeoutWithConnection() {
-        Call call = getCall();
-        call.setState(CallState.RINGING, "foo");
-        call.setIsCreateConnectionComplete(true);
-        call.setConnectionService(mMockConnectionService);
-        call.setIsVoipAudioMode(true);
-        call.setIsEmergencyCall(true);
-        mCallAnomalyWatchdog.onCallAdded(call);
+        setupCallHelper(CallState.RINGING, true, mMockConnectionService, true, true);
 
         // Newly created call which hasn't been added; should schedule timeout.
         assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -599,13 +566,7 @@
      */
     @Test
     public void testAddNonVoipRingingCallTimeoutWithConnection() {
-        Call call = getCall();
-        call.setState(CallState.RINGING, "foo");
-        call.setIsCreateConnectionComplete(true);
-        call.setConnectionService(mMockConnectionService);
-        call.setIsVoipAudioMode(false);
-        call.setIsEmergencyCall(false);
-        mCallAnomalyWatchdog.onCallAdded(call);
+        setupCallHelper(CallState.RINGING, true, mMockConnectionService, false, false);
 
         // Newly created call which hasn't been added; should schedule timeout.
         assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -628,13 +589,7 @@
      */
     @Test
     public void testAddNonVoipEmergencyRingingCallTimeoutWithConnection() {
-        Call call = getCall();
-        call.setState(CallState.RINGING, "foo");
-        call.setIsCreateConnectionComplete(true);
-        call.setConnectionService(mMockConnectionService);
-        call.setIsVoipAudioMode(false);
-        call.setIsEmergencyCall(true);
-        mCallAnomalyWatchdog.onCallAdded(call);
+        setupCallHelper(CallState.RINGING, true, mMockConnectionService, false, true);
 
         // Newly created call which hasn't been added; should schedule timeout.
         assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -659,12 +614,7 @@
     @Test
     public void testVoipPlaceCallTimeout() {
         // Call will start in connecting state
-        Call call = getCall();
-        call.setState(CallState.CONNECTING, "foo");
-        call.setIsCreateConnectionComplete(false);
-        call.setIsVoipAudioMode(true);
-        call.setIsEmergencyCall(false);
-        mCallAnomalyWatchdog.onCallCreated(call);
+        Call call = setupCallHelper(CallState.CONNECTING, false, null, true, false);
 
         // Assume it is created but the app never sets it to a proper state
         call.setIsCreateConnectionComplete(false);
@@ -682,18 +632,61 @@
     }
 
     /**
+     * Emulate the case where a new outgoing VoIP call is added to the watchdog.
+     * In this case, the timeout will fire in transitory state and should report an anomaly.
+     */
+    @Test
+    public void testVoipPlaceCallTimeoutReportAnomaly() {
+        // Call will start in connecting state
+        Call call = setupCallHelper(CallState.CONNECTING, false, null, true, false);
+
+        // Assume it is created but the app never sets it to a proper state
+        call.setIsCreateConnectionComplete(false);
+        mCallAnomalyWatchdog.onCallAdded(call);
+
+        // Move the clock to fire the timeout.
+        when(mMockClockProxy.elapsedRealtime()).thenReturn(TEST_VOIP_TRANSITORY_MILLIS + 1);
+        mTestScheduledExecutorService.advanceTime(TEST_VOIP_TRANSITORY_MILLIS + 1);
+
+        //Ensure an anomaly was reported
+        verify(mAnomalyReporterAdapter).reportAnomaly(
+                CallAnomalyWatchdog.WATCHDOG_DISCONNECTED_STUCK_CALL_UUID,
+                CallAnomalyWatchdog.WATCHDOG_DISCONNECTED_STUCK_CALL_MSG);
+    }
+
+    /**
+     * Emulate the case where a new outgoing VoIP emergency call is added to the watchdog.
+     * In this case, the timeout will fire in transitory state and should report an emergency
+     * anomaly.
+     */
+    @Test
+    public void testVoipEmergencyPlaceCallTimeoutReportAnomaly() {
+        // Call will start in connecting state
+        Call call = setupCallHelper(CallState.CONNECTING, false, null, true, true);
+
+        // Assume it is created but the app never sets it to a proper state
+        call.setIsCreateConnectionComplete(false);
+        mCallAnomalyWatchdog.onCallAdded(call);
+
+        // Move the clock to fire the timeout.
+        when(mMockClockProxy.elapsedRealtime()).
+                thenReturn(TEST_VOIP_EMERGENCY_TRANSITORY_MILLIS + 1);
+        mTestScheduledExecutorService.advanceTime(TEST_VOIP_EMERGENCY_TRANSITORY_MILLIS + 1);
+
+        //Ensure an anomaly was reported
+        verify(mAnomalyReporterAdapter).reportAnomaly(
+                CallAnomalyWatchdog.WATCHDOG_DISCONNECTED_STUCK_EMERGENCY_CALL_UUID,
+                CallAnomalyWatchdog.WATCHDOG_DISCONNECTED_STUCK_EMERGENCY_CALL_MSG);
+    }
+
+    /**
      * Emulate the case where a new outgoing VoIP emergency call is added to the watchdog.
      * In this case, the timeout will fire in transitory state.
      */
     @Test
     public void testVoipEmergencyPlaceCallTimeout() {
         // Call will start in connecting state
-        Call call = getCall();
-        call.setState(CallState.CONNECTING, "foo");
-        call.setIsCreateConnectionComplete(false);
-        call.setIsVoipAudioMode(true);
-        call.setIsEmergencyCall(true);
-        mCallAnomalyWatchdog.onCallCreated(call);
+        Call call = setupCallHelper(CallState.CONNECTING, false, null, true, true);
 
         // Assume it is created but the app never sets it to a proper state
         call.setIsCreateConnectionComplete(false);
@@ -718,12 +711,7 @@
     @Test
     public void testNonVoipPlaceCallTimeout() {
         // Call will start in connecting state
-        Call call = getCall();
-        call.setState(CallState.CONNECTING, "foo");
-        call.setIsCreateConnectionComplete(false);
-        call.setIsVoipAudioMode(false);
-        call.setIsEmergencyCall(false);
-        mCallAnomalyWatchdog.onCallCreated(call);
+        Call call = setupCallHelper(CallState.CONNECTING, false, null, false, false);
 
         // Assume it is created but the app never sets it to a proper state
         call.setIsCreateConnectionComplete(false);
@@ -747,12 +735,7 @@
     @Test
     public void testNonVoipEmergencyPlaceCallTimeout() {
         // Call will start in connecting state
-        Call call = getCall();
-        call.setState(CallState.CONNECTING, "foo");
-        call.setIsCreateConnectionComplete(false);
-        call.setIsVoipAudioMode(false);
-        call.setIsEmergencyCall(true);
-        mCallAnomalyWatchdog.onCallCreated(call);
+        Call call = setupCallHelper(CallState.CONNECTING, false, null, false, true);
 
         // Assume it is created but the app never sets it to a proper state
         call.setIsCreateConnectionComplete(false);
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index f754611..75a9a96 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -42,10 +42,12 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.Manifest;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -162,6 +164,13 @@
                     | PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
             .setIsEnabled(true)
             .build();
+    private static final PhoneAccount SIM_1_ACCOUNT_SECONDARY = new PhoneAccount
+            .Builder(SIM_1_HANDLE_SECONDARY, "Sim1")
+            .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
+                    | PhoneAccount.CAPABILITY_CALL_PROVIDER
+                    | PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
+            .setIsEnabled(true)
+            .build();
     private static final PhoneAccount SIM_2_ACCOUNT = new PhoneAccount.Builder(SIM_2_HANDLE, "Sim2")
             .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
                     | PhoneAccount.CAPABILITY_CALL_PROVIDER
@@ -2228,6 +2237,29 @@
         waitForCountDownLatch(latch);
     }
 
+    @SmallTest
+    @Test
+    public void testGetNumCallsWithState_MultiUser() throws Exception {
+        when(mContext.checkCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        // Add call under secondary user
+        Call call = addSpyCall(SIM_1_HANDLE_SECONDARY, CallState.ACTIVE);
+        when(call.getPhoneAccountFromHandle()).thenReturn(SIM_1_ACCOUNT_SECONDARY);
+        // Verify that call is visible to primary user
+        assertEquals(mCallsManager.getNumCallsWithState(0, null,
+                UserHandle.CURRENT_OR_SELF, true,
+                null, CallState.ACTIVE), 1);
+        // Verify that call is not visible to primary user
+        // when a different phone account handle is specified.
+        assertEquals(mCallsManager.getNumCallsWithState(0, null,
+                UserHandle.CURRENT_OR_SELF, true,
+                SIM_1_HANDLE, CallState.ACTIVE), 0);
+        // Deny INTERACT_ACROSS_USERS permission and verify that call is not visible to primary user
+        assertEquals(mCallsManager.getNumCallsWithState(0, null,
+                UserHandle.CURRENT_OR_SELF, false,
+                null, CallState.ACTIVE), 0);
+    }
+
     public void waitForCountDownLatch(CountDownLatch latch) throws InterruptedException {
             boolean success = latch.await(5000, TimeUnit.MILLISECONDS);
             if (!success) {
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index d71365c..0743805 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -75,6 +75,7 @@
 import android.telephony.TelephonyRegistryManager;
 import android.test.mock.MockContext;
 import android.util.DisplayMetrics;
+import android.view.accessibility.AccessibilityManager;
 
 import java.io.File;
 import java.io.IOException;
@@ -237,6 +238,8 @@
                     return mPermissionCheckerManager;
                 case Context.SENSOR_PRIVACY_SERVICE:
                     return mSensorPrivacyManager;
+                case Context.ACCESSIBILITY_SERVICE:
+                    return mAccessibilityManager;
                 default:
                     return null;
             }
@@ -270,6 +273,8 @@
                 return Context.SENSOR_PRIVACY_SERVICE;
             } else if (svcClass == NotificationManager.class) {
                 return Context.NOTIFICATION_SERVICE;
+            } else if (svcClass == AccessibilityManager.class) {
+                return Context.ACCESSIBILITY_SERVICE;
             }
             throw new UnsupportedOperationException();
         }
@@ -567,6 +572,7 @@
     private final LocationManager mLocationManager = mock(LocationManager.class);
     private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class);
     private final NotificationManager mNotificationManager = mock(NotificationManager.class);
+    private final AccessibilityManager mAccessibilityManager = mock(AccessibilityManager.class);
     private final UserManager mUserManager = mock(UserManager.class);
     private final StatusBarManager mStatusBarManager = mock(StatusBarManager.class);
     private SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
index 8407bc9..35eda43 100644
--- a/tests/src/com/android/server/telecom/tests/RingerTest.java
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -50,6 +50,7 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.view.accessibility.AccessibilityManager;
 
 import com.android.server.telecom.AsyncRingtonePlayer;
 import com.android.server.telecom.Call;
@@ -149,12 +150,13 @@
     @Mock Vibrator mockVibrator;
     @Mock InCallController mockInCallController;
     @Mock NotificationManager mockNotificationManager;
+    @Mock AccessibilityManager mockAccessibilityManager;
+
     @Spy Ringer.VibrationEffectProxy spyVibrationEffectProxy;
 
     @Mock InCallTonePlayer mockTonePlayer;
     @Mock Call mockCall1;
     @Mock Call mockCall2;
-    @Mock NotificationManager mNotificationManager;
 
     private static final PhoneAccountHandle PA_HANDLE =
             new PhoneAccountHandle(new ComponentName("pa_pkg", "pa_cls"),
@@ -174,26 +176,22 @@
             return new UriVibrationEffect(ringtoneUriForEffect);
         }).when(spyVibrationEffectProxy).get(any(), any());
         when(mockPlayerFactory.createPlayer(anyInt())).thenReturn(mockTonePlayer);
-        mockAudioManager =
-                (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-        mNotificationManager = mContext.getSystemService(NotificationManager.class);
-
+        mockAudioManager = mContext.getSystemService(AudioManager.class);
         when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         when(mockSystemSettingsUtil.isHapticPlaybackSupported(any(Context.class))).thenReturn(true);
-        mockNotificationManager =
-                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        mockNotificationManager =mContext.getSystemService(NotificationManager.class);
+        mockAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
         when(mockTonePlayer.startTone()).thenReturn(true);
         when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
         when(mockRingtoneFactory.hasHapticChannels(any(Ringtone.class))).thenReturn(false);
         mRingerUnderTest = new Ringer(mockPlayerFactory, mContext, mockSystemSettingsUtil,
                 mockRingtonePlayer, mockRingtoneFactory, mockVibrator, spyVibrationEffectProxy,
-                mockInCallController, mNotificationManager);
+                mockInCallController, mockNotificationManager, mockAccessibilityManager);
         when(mockCall1.getState()).thenReturn(CallState.RINGING);
         when(mockCall2.getState()).thenReturn(CallState.RINGING);
         when(mockCall1.getUserHandleFromTargetPhoneAccount()).thenReturn(PA_HANDLE.getUserHandle());
         when(mockCall2.getUserHandleFromTargetPhoneAccount()).thenReturn(PA_HANDLE.getUserHandle());
         mRingerUnderTest.setBlockOnRingingFuture(mRingCompletionFuture);
-        mRingerUnderTest.setNotificationManager(mockNotificationManager);
     }
 
     @Override
@@ -522,8 +520,9 @@
         when(mockCall1.wasDndCheckComputedForCall()).thenReturn(false);
         when(mockCall1.getHandle()).thenReturn(Uri.parse(""));
 
-        when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
-        when(mNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(false);
+        when(mContext.getSystemService(NotificationManager.class)).thenReturn(
+                mockNotificationManager);
+        when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(false);
 
         // THEN
         assertFalse(mRingerUnderTest.shouldRingForContact(mockCall1));
@@ -541,13 +540,60 @@
         // WHEN
         when(mockCall1.wasDndCheckComputedForCall()).thenReturn(false);
         when(mockCall1.getHandle()).thenReturn(Uri.parse(""));
-        when(mNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
+        when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
 
         // THEN
         assertTrue(mRingerUnderTest.shouldRingForContact(mockCall1));
         verify(mockCall1, atLeastOnce()).setCallIsSuppressedByDoNotDisturb(false);
     }
 
+    @Test
+    public void testNoFlashNotificationWhenCallSuppressed() {
+        ensureRingtoneMocked();
+        // Start call waiting to make sure that it doesn't stop when we start ringing
+        mRingerUnderTest.startCallWaiting(mockCall1);
+        when(mockCall2.wasDndCheckComputedForCall()).thenReturn(false);
+        when(mockCall2.getHandle()).thenReturn(Uri.parse(""));
+        when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(false);
+
+        assertFalse(mRingerUnderTest.shouldRingForContact(mockCall2));
+        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
+        verify(mockAccessibilityManager, never())
+                .startFlashNotificationSequence(any(Context.class), anyInt());
+    }
+
+    @Test
+    public void testStartFlashNotificationWhenRingStarts()  {
+        ensureRingtoneMocked();
+        // Start call waiting to make sure that it doesn't stop when we start ringing
+        mRingerUnderTest.startCallWaiting(mockCall1);
+        when(mockCall2.wasDndCheckComputedForCall()).thenReturn(false);
+        when(mockCall2.getHandle()).thenReturn(Uri.parse(""));
+        when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
+
+        assertTrue(mRingerUnderTest.shouldRingForContact(mockCall2));
+        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
+        verify(mockAccessibilityManager, atLeastOnce())
+                .startFlashNotificationSequence(any(Context.class), anyInt());
+    }
+
+    @Test
+    public void testStopFlashNotificationWhenRingStops() {
+        ensureRingtoneMocked();
+        // Start call waiting to make sure that it doesn't stop when we start ringing
+        mRingerUnderTest.startCallWaiting(mockCall1);
+        when(mockCall2.wasDndCheckComputedForCall()).thenReturn(false);
+        when(mockCall2.getHandle()).thenReturn(Uri.parse(""));
+        when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
+
+        assertTrue(mRingerUnderTest.shouldRingForContact(mockCall2));
+        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
+        mRingerUnderTest.stopRinging();
+        verify(mockAccessibilityManager, atLeastOnce())
+                .stopFlashNotificationSequence(any(Context.class));
+
+    }
+
     private void ensureRingerIsAudible() {
         when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(100);
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index a95d14e..569d6b4 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -24,6 +24,7 @@
 import static android.Manifest.permission.READ_PHONE_STATE;
 import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
 
+import android.Manifest;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.ComponentName;
@@ -219,6 +220,8 @@
         doReturn(mContext).when(mContext).createContextAsUser(any(UserHandle.class), anyInt());
         doNothing().when(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class),
                 anyString());
+        when(mContext.checkCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
         doAnswer(invocation -> {
             mDefaultDialerObserver = invocation.getArgument(1);
             return null;
@@ -1463,8 +1466,7 @@
     public void testEndCallWithNoForegroundCall() throws Exception {
         Call call = mock(Call.class);
         when(call.getState()).thenReturn(CallState.ACTIVE);
-        when(mFakeCallsManager.getFirstCallWithState(any()))
-                .thenReturn(call);
+        when(mFakeCallsManager.getFirstCallWithState(any())).thenReturn(call);
         assertTrue(mTSIBinder.endCall(TEST_PACKAGE));
         verify(mFakeCallsManager).disconnectCall(eq(call));
     }
@@ -1505,14 +1507,16 @@
     @SmallTest
     @Test
     public void testIsInCall() throws Exception {
-        when(mFakeCallsManager.hasOngoingCalls()).thenReturn(true);
+        when(mFakeCallsManager.hasOngoingCalls(any(UserHandle.class), anyBoolean()))
+                .thenReturn(true);
         assertTrue(mTSIBinder.isInCall(DEFAULT_DIALER_PACKAGE, null));
     }
 
     @SmallTest
     @Test
     public void testNotIsInCall() throws Exception {
-        when(mFakeCallsManager.hasOngoingCalls()).thenReturn(false);
+        when(mFakeCallsManager.hasOngoingCalls(any(UserHandle.class), anyBoolean()))
+                .thenReturn(false);
         assertFalse(mTSIBinder.isInCall(DEFAULT_DIALER_PACKAGE, null));
     }
 
@@ -1527,20 +1531,22 @@
         } catch (SecurityException e) {
             // desired result
         }
-        verify(mFakeCallsManager, never()).hasOngoingCalls();
+        verify(mFakeCallsManager, never()).hasOngoingCalls(any(UserHandle.class), anyBoolean());
     }
 
     @SmallTest
     @Test
     public void testIsInManagedCall() throws Exception {
-        when(mFakeCallsManager.hasOngoingManagedCalls()).thenReturn(true);
+        when(mFakeCallsManager.hasOngoingManagedCalls(any(UserHandle.class), anyBoolean()))
+                .thenReturn(true);
         assertTrue(mTSIBinder.isInManagedCall(DEFAULT_DIALER_PACKAGE, null));
     }
 
     @SmallTest
     @Test
     public void testNotIsInManagedCall() throws Exception {
-        when(mFakeCallsManager.hasOngoingManagedCalls()).thenReturn(false);
+        when(mFakeCallsManager.hasOngoingManagedCalls(any(UserHandle.class), anyBoolean()))
+                .thenReturn(false);
         assertFalse(mTSIBinder.isInManagedCall(DEFAULT_DIALER_PACKAGE, null));
     }
 
@@ -1555,7 +1561,7 @@
         } catch (SecurityException e) {
             // desired result
         }
-        verify(mFakeCallsManager, never()).hasOngoingCalls();
+        verify(mFakeCallsManager, never()).hasOngoingCalls(any(UserHandle.class), anyBoolean());
     }
 
     /**