Merge "enforcePhoneAccounts should unregister unresolvable accts" into main
diff --git a/src/com/android/server/telecom/AsyncRingtonePlayer.java b/src/com/android/server/telecom/AsyncRingtonePlayer.java
index 3fbac1f..912305b 100644
--- a/src/com/android/server/telecom/AsyncRingtonePlayer.java
+++ b/src/com/android/server/telecom/AsyncRingtonePlayer.java
@@ -30,6 +30,9 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
 
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.function.BiConsumer;
 import java.util.function.Supplier;
 
@@ -39,6 +42,9 @@
  */
 @VisibleForTesting
 public class AsyncRingtonePlayer {
+    // Maximum amount of time we will delay playing a ringtone while waiting for audio routing to
+    // be ready.
+    private static final int PLAY_DELAY_TIMEOUT_MS = 1000;
     // Message codes used with the ringtone thread.
     private static final int EVENT_PLAY = 1;
     private static final int EVENT_STOP = 2;
@@ -49,6 +55,23 @@
     /** The current ringtone. Only used by the ringtone thread. */
     private Ringtone mRingtone;
 
+    /**
+     * Set to true if we are setting up to play or are currently playing. False if we are stopping
+     * or have stopped playing.
+     */
+    private boolean mIsPlaying = false;
+
+    /**
+     * Set to true if BT HFP is active and audio is connected.
+     */
+    private boolean mIsBtActive = false;
+
+    /**
+     * A list of pending ringing ready latches, which are used to delay the ringing command until
+     * audio paths are set and ringing is ready.
+     */
+    private final ArrayList<CountDownLatch> mPendingRingingLatches = new ArrayList<>();
+
     public AsyncRingtonePlayer() {
         // Empty
     }
@@ -60,21 +83,81 @@
      *
      * @param ringtoneSupplier The {@link Ringtone} factory.
      * @param ringtoneConsumer The {@link Ringtone} post-creation callback (to start the vibration).
+     * @param isHfpDeviceConnected True if there is a HFP BT device connected, false otherwise.
      */
     public void play(@NonNull Supplier<Ringtone> ringtoneSupplier,
-            BiConsumer<Ringtone, Boolean> ringtoneConsumer) {
+            BiConsumer<Ringtone, Boolean> ringtoneConsumer,  boolean isHfpDeviceConnected) {
         Log.d(this, "Posting play.");
+        mIsPlaying = true;
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = ringtoneSupplier;
         args.arg2 = ringtoneConsumer;
         args.arg3 = Log.createSubsession();
+        args.arg4 = prepareRingingReadyLatch(isHfpDeviceConnected);
         postMessage(EVENT_PLAY, true /* shouldCreateHandler */, args);
     }
 
     /** Stops playing the ringtone. */
     public void stop() {
         Log.d(this, "Posting stop.");
+        mIsPlaying = false;
         postMessage(EVENT_STOP, false /* shouldCreateHandler */, null);
+        // Clear any pending ringing latches so that we do not have to wait for its timeout to pass
+        // before calling stop.
+        clearPendingRingingLatches();
+    }
+
+    /**
+     * Called when the BT HFP profile active state changes.
+     * @param isBtActive A BT device is connected and audio is active.
+     */
+    public void updateBtActiveState(boolean isBtActive) {
+        Log.i(this, "updateBtActiveState: " + isBtActive);
+        synchronized (mPendingRingingLatches) {
+            mIsBtActive = isBtActive;
+            if (isBtActive) mPendingRingingLatches.forEach(CountDownLatch::countDown);
+        }
+    }
+
+    /**
+     * Prepares a new ringing ready latch and tracks it in a list. Once the ready latch has been
+     * used, {@link #removePendingRingingReadyLatch(CountDownLatch)} must be called on this latch.
+     * @param isHfpDeviceConnected true if there is a HFP device connected.
+     * @return the newly prepared CountDownLatch
+     */
+    private CountDownLatch prepareRingingReadyLatch(boolean isHfpDeviceConnected) {
+        CountDownLatch latch = new CountDownLatch(1);
+        synchronized (mPendingRingingLatches) {
+            // We only want to delay ringing if BT is connected but not active yet.
+            boolean isDelayRequired = isHfpDeviceConnected && !mIsBtActive;
+            Log.i(this, "prepareRingingReadyLatch:"
+                    + " connected=" + isHfpDeviceConnected
+                    + ", BT active=" + mIsBtActive
+                    + ", isDelayRequired=" + isDelayRequired);
+            if (!isDelayRequired) latch.countDown();
+            mPendingRingingLatches.add(latch);
+        }
+        return latch;
+    }
+
+    /**
+     * Remove a ringing ready latch that has been used and is no longer pending.
+     * @param l The latch to remove.
+     */
+    private void removePendingRingingReadyLatch(CountDownLatch l) {
+        synchronized (mPendingRingingLatches) {
+            mPendingRingingLatches.remove(l);
+        }
+    }
+
+    /**
+     * Count down all pending ringing ready latches and then clear the list.
+     */
+    private void clearPendingRingingLatches() {
+        synchronized (mPendingRingingLatches) {
+            mPendingRingingLatches.forEach(CountDownLatch::countDown);
+            mPendingRingingLatches.clear();
+        }
     }
 
     /**
@@ -129,6 +212,7 @@
         Supplier<Ringtone> ringtoneSupplier = (Supplier<Ringtone>) args.arg1;
         BiConsumer<Ringtone, Boolean> ringtoneConsumer = (BiConsumer<Ringtone, Boolean>) args.arg2;
         Session session = (Session) args.arg3;
+        CountDownLatch ringingReadyLatch = (CountDownLatch) args.arg4;
         args.recycle();
 
         Log.continueSession(session, "ARP.hP");
@@ -136,17 +220,29 @@
             // Don't bother with any of this if there is an EVENT_STOP waiting, but give the
             // consumer a chance to do anything no matter what.
             if (mHandler.hasMessages(EVENT_STOP)) {
+                Log.i(this, "handlePlay: skipping play early due to pending STOP");
+                removePendingRingingReadyLatch(ringingReadyLatch);
                 ringtoneConsumer.accept(null, /* stopped= */ true);
                 return;
             }
             Ringtone ringtone = null;
             boolean hasStopped = false;
             try {
+                try {
+                    Log.i(this, "handlePlay: delay ring for ready signal...");
+                    boolean reachedZero = ringingReadyLatch.await(PLAY_DELAY_TIMEOUT_MS,
+                            TimeUnit.MILLISECONDS);
+                    Log.i(this, "handlePlay: ringing ready, timeout=" + !reachedZero);
+                } catch (InterruptedException e) {
+                    Log.w(this, "handlePlay: latch exception: " + e);
+                }
                 ringtone = ringtoneSupplier.get();
-                // Ringtone supply can be slow. Re-check for stop event.
+                // Ringtone supply can be slow or stop command could have been issued while waiting
+                // for BT to move to CONNECTED state. Re-check for stop event.
                 if (mHandler.hasMessages(EVENT_STOP)) {
+                    Log.i(this, "handlePlay: skipping play due to pending STOP");
                     hasStopped = true;
-                    ringtone.stop();  // proactively release the ringtone.
+                    if (ringtone != null) ringtone.stop();  // proactively release the ringtone.
                     return;
                 }
                 // setRingtone even if null - it also stops any current ringtone to be consistent
@@ -168,6 +264,7 @@
                 mRingtone.play();
                 Log.i(this, "Play ringtone, looping.");
             } finally {
+                removePendingRingingReadyLatch(ringingReadyLatch);
                 ringtoneConsumer.accept(ringtone, hasStopped);
             }
         } finally {
@@ -196,11 +293,15 @@
         }
     }
 
+    /**
+     * @return true if we are currently preparing or playing a ringtone, false if we are not.
+     */
     public boolean isPlaying() {
-        return mRingtone != null;
+        return mIsPlaying;
     }
 
     private void setRingtone(@Nullable Ringtone ringtone) {
+        Log.i(this, "setRingtone: ringtone null="  + (ringtone == null));
         // Make sure that any previously created instance of Ringtone is stopped so the MediaPlayer
         // can be released, before replacing mRingtone with a new instance. This is always created
         // as a looping Ringtone, so if not stopped it will keep playing on the background.
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 2454ba0..c32d2bf 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -362,7 +362,7 @@
 
     private PhoneAccountHandle mRemotePhoneAccountHandle;
 
-    private UserHandle mInitiatingUser;
+    private UserHandle mAssociatedUser;
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
 
@@ -830,8 +830,8 @@
                 ? PhoneNumberUtils.extractPostDialPortion(handle.getSchemeSpecificPart()) : "";
         mGatewayInfo = gatewayInfo;
         setConnectionManagerPhoneAccount(connectionManagerPhoneAccountHandle);
-        setTargetPhoneAccount(targetPhoneAccountHandle);
         mCallDirection = callDirection;
+        setTargetPhoneAccount(targetPhoneAccountHandle);
         mIsConference = isConference;
         mShouldAttachToExistingConnection = shouldAttachToExistingConnection
                 || callDirection == CALL_DIRECTION_INCOMING;
@@ -1005,7 +1005,7 @@
         s.append("]");
         s.append(isIncoming() ? "(MT - incoming)" : "(MO - outgoing)");
         s.append("(User=");
-        s.append(getInitiatingUser());
+        s.append(getAssociatedUser());
         s.append(")");
         s.append("\n\t");
 
@@ -1732,15 +1732,13 @@
             mCallStateChangedAtomWriter.setUid(
                     accountHandle.getComponentName().getPackageName(),
                     mContext.getPackageManager());
+            // Set the associated user for the call for MT calls based on the target phone account.
+            if (isIncoming() && !accountHandle.getUserHandle().equals(mAssociatedUser)) {
+                setAssociatedUser(accountHandle.getUserHandle());
+            }
         }
     }
 
-    public UserHandle getUserHandleFromTargetPhoneAccount() {
-        return mTargetPhoneAccountHandle == null
-                ? mCallsManager.getCurrentUserHandle() :
-                mTargetPhoneAccountHandle.getUserHandle();
-    }
-
     public PhoneAccount getPhoneAccountFromHandle() {
         if (getTargetPhoneAccount() == null) {
             return null;
@@ -1985,7 +1983,7 @@
         if (phoneAccount != null) {
             final UserHandle userHandle;
             if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
-                userHandle = mInitiatingUser;
+                userHandle = mAssociatedUser;
             } else {
                 userHandle = mTargetPhoneAccountHandle.getUserHandle();
             }
@@ -4017,19 +4015,26 @@
     }
 
     /**
-     * @return user handle of user initiating the outgoing call.
+     * It's possible that the target phone account isn't set when a user hasn't selected a
+     * default sim to place a call. Instead of using the user from the target phone account to
+     * associate the user with a call, we'll use mAssociatedUser instead. For MT calls, we will
+     * continue to use the target phone account user (as it's always set) and for MO calls, we will
+     * use the initiating user instead.
+     *
+     * @return user handle of user associated with the call.
      */
-    public UserHandle getInitiatingUser() {
-        return mInitiatingUser;
+    public UserHandle getAssociatedUser() {
+        return mAssociatedUser;
     }
 
     /**
-     * Set the user handle of user initiating the outgoing call.
-     * @param initiatingUser
+     * Set the user handle of user associated with the call.
+     * @param associatedUser
      */
-    public void setInitiatingUser(UserHandle initiatingUser) {
-        Preconditions.checkNotNull(initiatingUser);
-        mInitiatingUser = initiatingUser;
+    public void setAssociatedUser(UserHandle associatedUser) {
+        Log.i(this, "Setting associated user for call");
+        Preconditions.checkNotNull(associatedUser);
+        mAssociatedUser = associatedUser;
     }
 
     static int getStateFromConnectionState(int state) {
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index f52a966..6557dc6 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -36,6 +36,8 @@
 import java.util.HashSet;
 import java.util.Set;
 import java.util.LinkedHashSet;
+import java.util.stream.Collectors;
+
 
 public class CallAudioManager extends CallsManagerListenerBase {
 
@@ -116,7 +118,7 @@
             // State did not change, so no need to do anything.
             return;
         }
-        Log.d(LOG_TAG, "Call state changed for TC@%s: %s -> %s", call.getId(),
+        Log.i(this, "onCallStateChanged: Call state changed for TC@%s: %s -> %s", call.getId(),
                 CallState.toString(oldState), CallState.toString(newState));
 
         removeCallFromAllBins(call);
@@ -497,7 +499,7 @@
         boolean allCallSilenced = true;
         synchronized (mCallsManager.getLock()) {
             for (Call call : mRingingCalls) {
-                UserHandle userFromCall = call.getUserHandleFromTargetPhoneAccount();
+                UserHandle userFromCall = call.getAssociatedUser();
                 // Do not try to silence calls when calling user is different from the phone account
                 // user, the account does not have CAPABILITY_MULTI_USER enabled, or if the user
                 // does not have the INTERACT_ACROSS_USERS permission enabled.
@@ -761,6 +763,7 @@
 
     private void updateForegroundCall() {
         Call oldForegroundCall = mForegroundCall;
+
         if (mActiveDialingOrConnectingCalls.size() > 0) {
             // Give preference for connecting calls over active/dialing for foreground-ness.
             Call possibleConnectingCall = null;
@@ -769,8 +772,21 @@
                     possibleConnectingCall = call;
                 }
             }
-            mForegroundCall = possibleConnectingCall == null ?
-                    mActiveDialingOrConnectingCalls.iterator().next() : possibleConnectingCall;
+            // Prefer a connecting call
+            if (possibleConnectingCall != null) {
+                mForegroundCall = possibleConnectingCall;
+            } else {
+                // Next, prefer an active or dialing call which is not in the process of being
+                // disconnected.
+                mForegroundCall = mActiveDialingOrConnectingCalls
+                        .stream()
+                        .filter(c -> (c.getState() == CallState.ACTIVE
+                                || c.getState() == CallState.DIALING)
+                                && !c.isLocallyDisconnecting())
+                        .findFirst()
+                        // If we can't find one, then just fall back to the first one.
+                        .orElse(mActiveDialingOrConnectingCalls.iterator().next());
+            }
         } else if (mRingingCalls.size() > 0) {
             mForegroundCall = mRingingCalls.iterator().next();
         } else if (mHoldingCalls.size() > 0) {
@@ -778,10 +794,24 @@
         } else {
             mForegroundCall = null;
         }
-
+        Log.i(this, "updateForegroundCall; oldFg=%s, newFg=%s, aDC=%s, ring=%s, hold=%s",
+                (oldForegroundCall == null ? "none" : oldForegroundCall.getId()),
+                (mForegroundCall == null ? "none" : mForegroundCall.getId()),
+                mActiveDialingOrConnectingCalls.stream().map(c -> c.getId()).collect(
+                        Collectors.joining(",")),
+                mRingingCalls.stream().map(c -> c.getId()).collect(Collectors.joining(",")),
+                mHoldingCalls.stream().map(c -> c.getId()).collect(Collectors.joining(","))
+        );
         if (mForegroundCall != oldForegroundCall) {
             mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
                     CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
+
+            if (mForegroundCall != null) {
+                // Ensure the voip audio mode for the new foreground call is taken into account.
+                mCallAudioModeStateMachine.sendMessageWithArgs(
+                        CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE,
+                        makeArgsForModeStateMachine());
+            }
             mDtmfLocalTonePlayer.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
             maybePlayHoldTone();
         }
diff --git a/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java b/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java
index d96f953..af0757c 100644
--- a/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java
+++ b/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java
@@ -27,14 +27,17 @@
 
     private final CallAudioRouteStateMachine mCallAudioRouteStateMachine;
     private final BluetoothRouteManager mBluetoothRouteManager;
+    private final AsyncRingtonePlayer mRingtonePlayer;
 
     public CallAudioRoutePeripheralAdapter(
             CallAudioRouteStateMachine callAudioRouteStateMachine,
             BluetoothRouteManager bluetoothManager,
             WiredHeadsetManager wiredHeadsetManager,
-            DockManager dockManager) {
+            DockManager dockManager,
+            AsyncRingtonePlayer ringtonePlayer) {
         mCallAudioRouteStateMachine = callAudioRouteStateMachine;
         mBluetoothRouteManager = bluetoothManager;
+        mRingtonePlayer = ringtonePlayer;
 
         mBluetoothRouteManager.setListener(this);
         wiredHeadsetManager.addListener(this);
@@ -75,12 +78,22 @@
 
     @Override
     public void onBluetoothAudioConnected() {
+        mRingtonePlayer.updateBtActiveState(true);
+        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
+    }
+
+    @Override
+    public void onBluetoothAudioConnecting() {
+        mRingtonePlayer.updateBtActiveState(false);
+        // Pretend like audio is connected when communicating w/ CARSM.
         mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
                 CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
     }
 
     @Override
     public void onBluetoothAudioDisconnected() {
+        mRingtonePlayer.updateBtActiveState(false);
         mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
                 CallAudioRouteStateMachine.BT_AUDIO_DISCONNECTED);
     }
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index a709040..a817302 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -918,7 +918,7 @@
                     if (msg.arg1 == NO_FOCUS) {
                         // Only disconnect audio here instead of routing away from BT entirely.
                         mBluetoothRouteManager.disconnectAudio();
-                        reinitialize();
+                        transitionTo(mQuiescentBluetoothRoute);
                         mCallAudioManager.notifyAudioOperationsComplete();
                     } else if (msg.arg1 == RINGING_FOCUS
                             && !mBluetoothRouteManager.isInbandRingingEnabled()) {
@@ -1772,17 +1772,15 @@
         final boolean hasAnyCalls = mCallsManager.hasAnyCalls();
         // These APIs are all via two-way binder calls so can potentially block Telecom.  Since none
         // of this has to happen in the Telecom lock we'll offload it to the async executor.
-        mAsyncTaskExecutor.execute(() -> {
-            boolean speakerOn = false;
-            if (on) {
-                speakerOn = mCommunicationDeviceTracker.setCommunicationDevice(
-                        AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, null);
-            } else {
-                mCommunicationDeviceTracker.clearCommunicationDevice(
-                        AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
-            }
-            mStatusBarNotifier.notifySpeakerphone(hasAnyCalls && speakerOn);
-        });
+        boolean speakerOn = false;
+        if (on) {
+            speakerOn = mCommunicationDeviceTracker.setCommunicationDevice(
+                    AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, null);
+        } else {
+            mCommunicationDeviceTracker.clearCommunicationDevice(
+                    AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
+        }
+        mStatusBarNotifier.notifySpeakerphone(hasAnyCalls && speakerOn);
     }
 
     private void setBluetoothOn(String address) {
diff --git a/src/com/android/server/telecom/CallEndpointController.java b/src/com/android/server/telecom/CallEndpointController.java
index 82164b3..4738cd4 100644
--- a/src/com/android/server/telecom/CallEndpointController.java
+++ b/src/com/android/server/telecom/CallEndpointController.java
@@ -87,7 +87,7 @@
     }
 
     public void requestCallEndpointChange(CallEndpoint endpoint, ResultReceiver callback) {
-        Log.d(this, "requestCallEndpointChange %s", endpoint);
+        Log.i(this, "requestCallEndpointChange %s", endpoint);
         int route = mTypeToRouteMap.get(endpoint.getEndpointType());
         String bluetoothAddress = getBluetoothAddress(endpoint);
 
@@ -99,7 +99,6 @@
         }
 
         if (isCurrentEndpointRequestedEndpoint(route, bluetoothAddress)) {
-            Log.d(this, "requestCallEndpointChange: requested endpoint is already active");
             callback.send(CallEndpoint.ENDPOINT_OPERATION_SUCCESS, new Bundle());
             return;
         }
@@ -130,21 +129,27 @@
             return false;
         }
         CallAudioState currentAudioState = mCallsManager.getCallAudioManager().getCallAudioState();
-        // requested non-bt endpoint is already active
-        if (requestedRoute != CallAudioState.ROUTE_BLUETOOTH &&
-                requestedRoute == currentAudioState.getRoute()) {
-            return true;
-        }
-        // requested bt endpoint is already active
-        if (requestedRoute == CallAudioState.ROUTE_BLUETOOTH &&
-                currentAudioState.getActiveBluetoothDevice() != null &&
-                requestedAddress.equals(
-                        currentAudioState.getActiveBluetoothDevice().getAddress())) {
-            return true;
+        if (requestedRoute == currentAudioState.getRoute()) {
+            if (requestedRoute != CallAudioState.ROUTE_BLUETOOTH) {
+                // The audio route (earpiece, speaker, etc.) is already active
+                // and Telecom can ignore the spam request!
+                Log.i(this, "iCERE: user requested a non-BT route that is already active");
+                return true;
+            } else if (hasSameBluetoothAddress(currentAudioState, requestedAddress)) {
+                // if the requested (BT route, device) is active, ignore the request...
+                Log.i(this, "iCERE: user requested a BT endpoint that is already active");
+                return true;
+            }
         }
         return false;
     }
 
+    public boolean hasSameBluetoothAddress(CallAudioState audioState, String requestedAddress) {
+        boolean hasActiveBtDevice = audioState.getActiveBluetoothDevice() != null;
+        return hasActiveBtDevice && requestedAddress.equals(
+                audioState.getActiveBluetoothDevice().getAddress());
+    }
+
     private Bundle getErrorResult(int result) {
         String message;
         int resultCode;
@@ -247,8 +252,9 @@
                     for (BluetoothDevice device : state.getSupportedBluetoothDevices()) {
                         CallEndpoint endpoint = findMatchingBluetoothEndpoint(device);
                         if (endpoint == null) {
+                            String deviceName = device.getName();
                             endpoint = new CallEndpoint(
-                                    device.getName() != null ? device.getName() : "",
+                                    deviceName != null ? deviceName : "",
                                     CallEndpoint.TYPE_BLUETOOTH);
                         }
                         newAvailableEndpoints.add(endpoint);
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index a7bbf84..3005656 100644
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -324,7 +324,7 @@
         }
 
         PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked(accountHandle);
-        UserHandle initiatingUser = call.getInitiatingUser();
+        UserHandle initiatingUser = call.getAssociatedUser();
         if (phoneAccount != null &&
                 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
             if (initiatingUser != null &&
diff --git a/src/com/android/server/telecom/CallStreamingController.java b/src/com/android/server/telecom/CallStreamingController.java
index 0cf5120..1323633 100644
--- a/src/com/android/server/telecom/CallStreamingController.java
+++ b/src/com/android/server/telecom/CallStreamingController.java
@@ -182,7 +182,7 @@
             super(mTelecomLock);
             mWrapper = wrapper;
             mCall = call;
-            mUserHandle = mCall.getInitiatingUser();
+            mUserHandle = mCall.getAssociatedUser();
             mContext = context;
         }
 
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index b0db65c..f38d8a1 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -353,6 +353,16 @@
             new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
 
     /**
+     * List of self-managed calls that have been initialized but not yet added to
+     * CallsManager#addCall(Call). There is a window of time when a Call has been added to Telecom
+     * (e.g. TelecomManager#addNewIncomingCall) to actually added in CallsManager#addCall(Call).
+     * This list is helpful for the NotificationManagerService to know that Telecom is currently
+     * setting up a call which is an important set in making notifications non-dismissible.
+     */
+    private final Set<Call> mSelfManagedCallsBeingSetup = Collections.newSetFromMap(
+            new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
+
+    /**
      * A pending call is one which requires user-intervention in order to be placed.
      * Used by {@link #startCallConfirmation}.
      */
@@ -562,6 +572,7 @@
             CallAnomalyWatchdog callAnomalyWatchdog,
             Ringer.AccessibilityManagerAdapter accessibilityManagerAdapter,
             Executor asyncTaskExecutor,
+            Executor asyncCallAudioTaskExecutor,
             BlockedNumbersAdapter blockedNumbersAdapter,
             TransactionManager transactionManager,
             EmergencyCallDiagnosticLogger emergencyCallDiagnosticLogger,
@@ -597,7 +608,7 @@
                         statusBarNotifier,
                         audioServiceFactory,
                         CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT,
-                        asyncTaskExecutor,
+                        asyncCallAudioTaskExecutor,
                         communicationDeviceTracker
                 );
         callAudioRouteStateMachine.initialize();
@@ -607,7 +618,8 @@
                         callAudioRouteStateMachine,
                         bluetoothManager,
                         wiredHeadsetManager,
-                        mDockManager);
+                        mDockManager,
+                        asyncRingtonePlayer);
         AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
         InCallTonePlayer.MediaPlayerFactory mediaPlayerFactory =
                 (resourceId, attributes) ->
@@ -738,7 +750,7 @@
     public void onSuccessfulOutgoingCall(Call call, int callState) {
         Log.v(this, "onSuccessfulOutgoingCall, %s", call);
         call.setPostCallPackageName(getRoleManagerAdapter().getDefaultCallScreeningApp(
-                call.getUserHandleFromTargetPhoneAccount()));
+                call.getAssociatedUser()));
 
         setCallState(call, callState, "successful outgoing call");
         if (!mCalls.contains(call)) {
@@ -802,7 +814,7 @@
     private IncomingCallFilterGraph setUpCallFilterGraph(Call incomingCall) {
         incomingCall.setIsUsingCallFiltering(true);
         String carrierPackageName = getCarrierPackageName();
-        UserHandle userHandle = incomingCall.getUserHandleFromTargetPhoneAccount();
+        UserHandle userHandle = incomingCall.getAssociatedUser();
         String defaultDialerPackageName = TelecomManager.from(mContext).
                 getDefaultDialerPackage(userHandle);
         String userChosenPackageName = getRoleManagerAdapter().
@@ -924,7 +936,7 @@
         if (result.shouldAllowCall) {
             incomingCall.setPostCallPackageName(
                     getRoleManagerAdapter().getDefaultCallScreeningApp(
-                            incomingCall.getUserHandleFromTargetPhoneAccount()
+                            incomingCall.getAssociatedUser()
                     ));
 
             Log.i(this, "onCallFilteringComplete: allow call.");
@@ -1400,6 +1412,8 @@
             // Required for backwards compatibility
             handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
         }
+        PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
+                phoneAccountHandle);
         Call call = new Call(
                 generateNextCallId(extras),
                 mContext,
@@ -1416,6 +1430,15 @@
                 isConference, /* isConference */
                 mClockProxy,
                 mToastFactory);
+        // Ensure new calls related to self-managed calls/connections are set as such. This will
+        // be overridden when the actual connection is returned in startCreateConnection, however
+        // doing this now ensures the logs and any other logic will treat this call as self-managed
+        // from the moment it is created.
+        boolean isSelfManaged = phoneAccount != null && phoneAccount.isSelfManaged();
+        call.setIsSelfManaged(isSelfManaged);
+        // It's important to start tracking self-managed calls as soon as the Call object is
+        // initialized so NotificationManagerService is aware Telecom is setting up a call
+        if (isSelfManaged) mSelfManagedCallsBeingSetup.add(call);
 
         // set properties for transactional call
         if (extras.containsKey(TelecomManager.TRANSACTION_CALL_ID_KEY)) {
@@ -1433,18 +1456,11 @@
                 }
             }
             // Incoming address was set via EXTRA_INCOMING_CALL_ADDRESS above.
-            call.setInitiatingUser(phoneAccountHandle.getUserHandle());
+            call.setAssociatedUser(phoneAccountHandle.getUserHandle());
         }
 
-        // Ensure new calls related to self-managed calls/connections are set as such. This will
-        // be overridden when the actual connection is returned in startCreateConnection, however
-        // doing this now ensures the logs and any other logic will treat this call as self-managed
-        // from the moment it is created.
-        PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
-                phoneAccountHandle);
         if (phoneAccount != null) {
             Bundle phoneAccountExtras = phoneAccount.getExtras();
-            call.setIsSelfManaged(phoneAccount.isSelfManaged());
             if (call.isSelfManaged()) {
                 // Self managed calls will always be voip audio mode.
                 call.setIsVoipAudioMode(true);
@@ -1560,7 +1576,14 @@
         // Check if the target phone account is possibly in ECBM.
         call.setIsInECBM(getEmergencyCallHelper()
                 .isLastOutgoingEmergencyCallPAH(call.getTargetPhoneAccount()));
-        if (mUserManager.isQuietModeEnabled(call.getUserHandleFromTargetPhoneAccount())
+        // If the phone account user profile is paused or the call isn't visible to the secondary/
+        // guest user, reject the non-emergency incoming call. When the current user is the admin,
+        // we need to allow the calls to go through if the work profile isn't paused. We should
+        // always allow emergency calls and also allow non-emergency calls when ECBM is active for
+        // the phone account.
+        if ((mUserManager.isQuietModeEnabled(call.getAssociatedUser())
+                || (!mUserManager.isUserAdmin(mCurrentUserHandle.getIdentifier())
+                && !isCallVisibleForUser(call, mCurrentUserHandle)))
                 && !call.isEmergencyCall() && !call.isInECBM()) {
             Log.d(TAG, "Rejecting non-emergency call because the owner %s is not running.",
                     phoneAccountHandle.getUserHandle());
@@ -1637,6 +1660,8 @@
                 mToastFactory);
         call.initAnalytics();
 
+        // For unknown calls, base the associated user off of the target phone account handle.
+        call.setAssociatedUser(phoneAccountHandle.getUserHandle());
         setIntentExtrasAndStartTime(call, extras);
         call.addListener(this);
         notifyStartCreateConnection(call);
@@ -1745,7 +1770,7 @@
                     isConference ? participants : null,
                     null /* gatewayInfo */,
                     null /* connectionManagerPhoneAccount */,
-                    null /* requestedAccountHandle */,
+                    requestedAccountHandle /* targetPhoneAccountHandle */,
                     Call.CALL_DIRECTION_OUTGOING /* callDirection */,
                     false /* forceAttachToExistingConnection */,
                     isConference, /* isConference */
@@ -1766,7 +1791,6 @@
                                 TelecomManager.PRESENTATION_ALLOWED);
                     }
                 }
-                call.setTargetPhoneAccount(requestedAccountHandle);
             }
 
             call.initAnalytics(callingPackage, creationLogs.toString());
@@ -1800,11 +1824,14 @@
                         || phoneAccountExtra.getBoolean(
                                 PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, true));
             }
-            call.setInitiatingUser(initiatingUser);
+            call.setAssociatedUser(initiatingUser);
             isReusedCall = false;
         } else {
             isReusedCall = true;
         }
+        // It's important to start tracking self-managed calls as soon as the Call object is
+        // initialized so NotificationManagerService is aware Telecom is setting up a call
+        if (isSelfManaged) mSelfManagedCallsBeingSetup.add(call);
 
         int videoState = VideoProfile.STATE_AUDIO_ONLY;
         if (extras != null) {
@@ -2076,7 +2103,7 @@
                     (callPhoneAccountHandlePair, uriCallerInfoPair) -> {
                         Call theCall = callPhoneAccountHandlePair.first;
                         UserHandle userHandleForCallScreening = theCall.
-                                getUserHandleFromTargetPhoneAccount();
+                                getAssociatedUser();
                         boolean isInContacts = uriCallerInfoPair.second != null
                                 && uriCallerInfoPair.second.contactExists;
                         Log.d(CallsManager.this, "outgoingCallIdStage: isInContacts=%s",
@@ -2311,7 +2338,7 @@
         // Find the user chosen call screening app.
         String callScreeningApp =
                 mRoleManagerAdapter.getDefaultCallScreeningApp(
-                        theCall.getUserHandleFromTargetPhoneAccount());
+                        theCall.getAssociatedUser());
 
         CompletableFuture future =
                 new CallScreeningServiceHelper(mContext,
@@ -2475,7 +2502,7 @@
                 && !phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
             // Note that mCurrentUserHandle may not actually be the current user, i.e.
             // in the case of work profiles
-            UserHandle currentUserHandle = call.getUserHandleFromTargetPhoneAccount();
+            UserHandle currentUserHandle = call.getAssociatedUser();
             // Check if the phoneAccountHandle belongs to the current user
             if (phoneAccountHandle != null &&
                     !phoneAccountHandle.getUserHandle().equals(currentUserHandle)) {
@@ -2758,7 +2785,7 @@
         // Auto-enable speakerphone if the originating intent specified to do so, if the call
         // is a video call, of if using speaker when docked
         PhoneAccount account = mPhoneAccountRegistrar.getPhoneAccount(
-                call.getTargetPhoneAccount(), call.getInitiatingUser());
+                call.getTargetPhoneAccount(), call.getAssociatedUser());
         boolean allowVideo = false;
         if (account != null) {
             allowVideo = account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING);
@@ -2820,7 +2847,7 @@
             }
         } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
                 requireCallCapableAccountByHandle ? callHandleScheme : null, false,
-                call.getInitiatingUser(), false).isEmpty()) {
+                call.getAssociatedUser(), false).isEmpty()) {
             // If there are no call capable accounts, disconnect the call.
             markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED,
                     "No registered PhoneAccounts"));
@@ -3439,7 +3466,7 @@
         } else {
             if (setDefault) {
                 mPhoneAccountRegistrar
-                        .setUserSelectedOutgoingPhoneAccount(account, call.getInitiatingUser());
+                        .setUserSelectedOutgoingPhoneAccount(account, call.getAssociatedUser());
             }
 
             if (mPendingAccountSelection != null) {
@@ -4145,6 +4172,8 @@
         call.setVideoProvider(parcelableConference.getVideoProvider());
         call.setStatusHints(parcelableConference.getStatusHints());
         call.putConnectionServiceExtras(parcelableConference.getExtras());
+        // For conference calls, set the associated user from the target phone account user handle.
+        call.setAssociatedUser(phoneAccount.getUserHandle());
         // In case this Conference was added via a ConnectionManager, keep track of the original
         // Connection ID as created by the originating ConnectionService.
         Bundle extras = parcelableConference.getExtras();
@@ -4237,6 +4266,7 @@
         Log.i(this, "addCall(%s)", call);
         call.addListener(this);
         mCalls.add(call);
+        mSelfManagedCallsBeingSetup.remove(call);
 
         // Specifies the time telecom finished routing the call. This is used by the dialer for
         // analytics.
@@ -4280,6 +4310,7 @@
             mCalls.remove(call);
             shouldNotify = true;
         }
+        mSelfManagedCallsBeingSetup.remove(call);
 
         call.destroy();
         updateExternalCallCanPullSupport();
@@ -4537,8 +4568,10 @@
      * @return {@code true} if the app has ongoing calls, or {@code false} otherwise.
      */
     public boolean isInSelfManagedCall(String packageName, UserHandle userHandle) {
-        return mCalls.stream().anyMatch(
-                c -> c.isSelfManaged()
+        return mSelfManagedCallsBeingSetup.stream().anyMatch(c -> c.isSelfManaged()
+                && c.getTargetPhoneAccount().getComponentName().getPackageName().equals(packageName)
+                && c.getTargetPhoneAccount().getUserHandle().equals(userHandle)) ||
+                mCalls.stream().anyMatch(c -> c.isSelfManaged()
                 && c.getTargetPhoneAccount().getComponentName().getPackageName().equals(packageName)
                 && c.getTargetPhoneAccount().getUserHandle().equals(userHandle));
     }
@@ -4706,15 +4739,21 @@
 
     /**
      * Determines the number of unholdable calls present in a connection service other than the one
-     * the passed phone account belonds to.
+     * the passed phone account belongs to. If a ConnectionService has not been associated with an
+     * outgoing call yet (for example, it is in the SELECT_PHONE_ACCOUNT state), then we do not
+     * count that call because it is not tracked as an active call yet.
      * @param phoneAccountHandle The handle of the PhoneAccount.
      * @return Number of unholdable calls owned by other connection service.
      */
     public int getNumUnholdableCallsForOtherConnectionService(
             PhoneAccountHandle phoneAccountHandle) {
         return (int) mCalls.stream().filter(call ->
-                !phoneAccountHandle.getComponentName().equals(
-                        call.getTargetPhoneAccount().getComponentName())
+                // If this convention needs to be changed, answerCall will need to be modified to
+                // change what an "active call" is so that the call in SELECT_PHONE_ACCOUNT state
+                // will be properly cancelled.
+                call.getTargetPhoneAccount() != null
+                        && !phoneAccountHandle.getComponentName().equals(
+                                call.getTargetPhoneAccount().getComponentName())
                         && call.getParentCall() == null
                         && !call.isExternalCall()
                         && !canHold(call)).count();
@@ -4730,11 +4769,14 @@
     }
 
     /**
-     * Determines if there are any self-managed calls.
+     * Note: isInSelfManagedCall(packageName, UserHandle) should always be used in favor or this
+     * method. This method determines if there are any self-managed calls globally.
      * @return {@code true} if there are self-managed calls, {@code false} otherwise.
      */
+    @VisibleForTesting
     public boolean hasSelfManagedCalls() {
-        return mCalls.stream().filter(call -> call.isSelfManaged()).count() > 0;
+        return mSelfManagedCallsBeingSetup.size() > 0 ||
+                mCalls.stream().filter(call -> call.isSelfManaged()).count() > 0;
     }
 
     /**
@@ -5182,6 +5224,9 @@
         call.setHandle(connection.getHandle(), connection.getHandlePresentation());
         call.setCallerDisplayName(connection.getCallerDisplayName(),
                 connection.getCallerDisplayNamePresentation());
+        // For existing connections, use the phone account user handle to determine the user
+        // association with the call.
+        call.setAssociatedUser(connection.getPhoneAccount().getUserHandle());
         call.addListener(this);
         call.putConnectionServiceExtras(connection.getExtras());
 
@@ -5831,7 +5876,9 @@
         if (isSelfManaged) {
             call.setIsVoipAudioMode(true);
         }
-        call.setInitiatingUser(getCurrentUserHandle());
+        // Set associated user based on the existing call as it doesn't make sense to handover calls
+        // across user profiles.
+        call.setAssociatedUser(handoverFromCall.getAssociatedUser());
 
         // Ensure we don't try to place an outgoing call with video if video is not
         // supported.
@@ -6065,6 +6112,9 @@
         fromCall.setHandoverDestinationCall(call);
         call.setHandoverSourceCall(fromCall);
         call.setHandoverState(HandoverState.HANDOVER_TO_STARTED);
+        // Set associated user based on the existing call as it doesn't make sense to handover calls
+        // across user profiles.
+        call.setAssociatedUser(fromCall.getAssociatedUser());
         fromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED);
 
         if (isSpeakerEnabledForVideoCalls() && VideoProfile.isVideo(videoState)) {
@@ -6351,7 +6401,7 @@
      * @return {@code true} if call is visible to the calling user
      */
     boolean isCallVisibleForUser(Call call, UserHandle userHandle) {
-        return call.getUserHandleFromTargetPhoneAccount().equals(userHandle)
+        return call.getAssociatedUser().equals(userHandle)
                 || call.getPhoneAccountFromHandle()
                 .hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER);
     }
@@ -6481,4 +6531,14 @@
         }
         call.getTransactionServiceWrapper().stopCallStreaming(call);
     }
+
+    @VisibleForTesting
+    public Set<Call> getSelfManagedCallsBeingSetup() {
+        return mSelfManagedCallsBeingSetup;
+    }
+
+    @VisibleForTesting
+    public void addCallBeingSetup(Call call) {
+        mSelfManagedCallsBeingSetup.add(call);
+    }
 }
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index 6702f03..19691c1 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -391,7 +391,7 @@
             // current user.
             // ONLY include phone accounts which are NOT self-managed; we will never consider a self
             // managed phone account for placing an emergency call.
-            UserHandle userFromCall = mCall.getUserHandleFromTargetPhoneAccount();
+            UserHandle userFromCall = mCall.getAssociatedUser();
             List<PhoneAccount> allAccounts = mPhoneAccountRegistrar
                     .getAllPhoneAccounts(userFromCall, false)
                     .stream()
diff --git a/src/com/android/server/telecom/HeadsetMediaButton.java b/src/com/android/server/telecom/HeadsetMediaButton.java
index 8e9caff..7458f54 100644
--- a/src/com/android/server/telecom/HeadsetMediaButton.java
+++ b/src/com/android/server/telecom/HeadsetMediaButton.java
@@ -23,16 +23,11 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.telecom.CallAudioState;
-import android.telecom.CallEndpoint;
 import android.telecom.Log;
-import android.util.ArraySet;
 import android.view.KeyEvent;
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.util.Set;
-
 /**
  * Static class to handle listening to the headset media buttons.
  */
@@ -154,10 +149,8 @@
     private final Context mContext;
     private final CallsManager mCallsManager;
     private final TelecomSystem.SyncRoot mLock;
-    private final Set<Call> mCalls = new ArraySet<>();
     private MediaSessionAdapter mSession;
     private KeyEvent mLastHookEvent;
-    private @CallEndpoint.EndpointType int mCurrentEndpointType;
 
     /**
      * Constructor used for testing purposes to initialize a {@link HeadsetMediaButton} with a
@@ -219,7 +212,7 @@
             return mCallsManager.onMediaButton(LONG_PRESS);
         } else if (event.getAction() == KeyEvent.ACTION_UP) {
             // We should not judge SHORT_PRESS by ACTION_UP event repeatCount, because it always
-            // returns 0.
+            // return 0.
             // Actually ACTION_DOWN event repeatCount only increases when LONG_PRESS performed.
             if (mLastHookEvent != null && mLastHookEvent.getRepeatCount() == 0) {
                 return mCallsManager.onMediaButton(SHORT_PRESS);
@@ -233,70 +226,50 @@
         return true;
     }
 
-    @Override
-    public void onCallEndpointChanged(CallEndpoint callEndpoint) {
-        mCurrentEndpointType = callEndpoint.getEndpointType();
-        Log.i(this, "onCallEndpointChanged: endPoint=%s", callEndpoint);
-        maybeChangeSessionState();
-    }
-
     /** ${inheritDoc} */
     @Override
     public void onCallAdded(Call call) {
-        handleCallAddition(call);
+        if (call.isExternalCall()) {
+            return;
+        }
+        handleCallAddition();
     }
 
     /**
      * Triggers session activation due to call addition.
      */
-    private void handleCallAddition(Call call) {
-        mCalls.add(call);
-        maybeChangeSessionState();
+    private void handleCallAddition() {
+        mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 1, 0).sendToTarget();
+    }
+
+    /** ${inheritDoc} */
+    @Override
+    public void onCallRemoved(Call call) {
+        if (call.isExternalCall()) {
+            return;
+        }
+        handleCallRemoval();
     }
 
     /**
-     * Based on whether there are tracked calls and the audio is routed to a wired headset,
-     * potentially activate or deactive the media session.
+     * Triggers session deactivation due to call removal.
      */
-    private void maybeChangeSessionState() {
-        boolean hasNonExternalCalls = !mCalls.isEmpty()
-                && mCalls.stream().anyMatch(c -> !c.isExternalCall());
-        if (hasNonExternalCalls && mCurrentEndpointType == CallEndpoint.TYPE_WIRED_HEADSET) {
-            Log.i(this, "maybeChangeSessionState: hasCalls=%b, currentEndpointType=%s, ACTIVATE",
-                    hasNonExternalCalls, CallEndpoint.endpointTypeToString(mCurrentEndpointType));
-            mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 1, 0).sendToTarget();
-        } else {
-            Log.i(this, "maybeChangeSessionState: hasCalls=%b, currentEndpointType=%s, DEACTIVATE",
-                    hasNonExternalCalls, CallEndpoint.endpointTypeToString(mCurrentEndpointType));
+    private void handleCallRemoval() {
+        if (!mCallsManager.hasAnyCalls()) {
             mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 0, 0).sendToTarget();
         }
     }
 
     /** ${inheritDoc} */
     @Override
-    public void onCallRemoved(Call call) {
-        handleCallRemoval(call);
-    }
-
-    /**
-     * Triggers session deactivation due to call removal.
-     */
-    private void handleCallRemoval(Call call) {
-        // If we were tracking the call, potentially change session state.
-        if (mCalls.remove(call)) {
-            if (mCalls.isEmpty()) {
-                // When there are no calls, don't cache that we previously had a wired headset
-                // connected; we'll be updated on the next call.
-                mCurrentEndpointType = CallEndpoint.TYPE_UNKNOWN;
-            }
-            maybeChangeSessionState();
-        }
-    }
-
-    /** ${inheritDoc} */
-    @Override
     public void onExternalCallChanged(Call call, boolean isExternalCall) {
-        maybeChangeSessionState();
+        // Note: We don't use the onCallAdded/onCallRemoved methods here since they do checks to see
+        // if the call is external or not and would skip the session activation/deactivation.
+        if (isExternalCall) {
+            handleCallRemoval();
+        } else {
+            handleCallAddition();
+        }
     }
 
     @VisibleForTesting
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index a952fba..598664e 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -47,6 +47,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.permission.PermissionManager;
 import android.telecom.CallAudioState;
 import android.telecom.CallEndpoint;
 import android.telecom.ConnectionService;
@@ -146,16 +147,20 @@
         private long mBindingStartTime;
         private long mDisconnectTime;
 
+        private boolean mHasCrossUserOrProfilePerm;
+
         public InCallServiceInfo(ComponentName componentName,
                 boolean isExternalCallsSupported,
                 boolean isSelfManageCallsSupported,
-                int type) {
+                int type, boolean hasCrossUserOrProfilePerm) {
             mComponentName = componentName;
             mIsExternalCallsSupported = isExternalCallsSupported;
             mIsSelfManagedCallsSupported = isSelfManageCallsSupported;
             mType = type;
+            mHasCrossUserOrProfilePerm = hasCrossUserOrProfilePerm;
         }
 
+        public boolean hasCrossUserOrProfilePermission() { return mHasCrossUserOrProfilePerm; }
         public ComponentName getComponentName() {
             return mComponentName;
         }
@@ -292,8 +297,19 @@
         private boolean mIsNullBinding = false;
         private NotificationManager mNotificationManager;
 
+        //this is really used for cases where the userhandle for a call
+        //does not match what we want to use for bindAsUser
+        private final UserHandle mUserHandleToUseForBinding;
+
         public InCallServiceBindingConnection(InCallServiceInfo info) {
             mInCallServiceInfo = info;
+            mUserHandleToUseForBinding = null;
+        }
+
+        public InCallServiceBindingConnection(InCallServiceInfo info,
+                UserHandle userHandleToUseForBinding) {
+            mInCallServiceInfo = info;
+            mUserHandleToUseForBinding = userHandleToUseForBinding;
         }
 
         @Override
@@ -335,14 +351,31 @@
             Log.i(this, "Attempting to bind to InCall %s, with %s", mInCallServiceInfo, intent);
             mIsConnected = true;
             mInCallServiceInfo.setBindingStartTime(mClockProxy.elapsedRealtime());
-            UserHandle userToBind = getUserFromCall(call);
-            boolean isManagedProfile = UserUtil.isManagedProfile(mContext, userToBind);
+            boolean isManagedProfile = UserUtil.isManagedProfile(mContext, userFromCall);
             // Note that UserHandle.CURRENT fails to capture the work profile, so we need to handle
             // it separately to ensure that the ICS is bound to the appropriate user. If ECBM is
             // active, we know that a work sim was previously used to place a MO emergency call. We
             // need to ensure that we bind to the CURRENT_USER in this case, as the work user would
             // not be running (handled in getUserFromCall).
-            userToBind = isManagedProfile ? userToBind : UserHandle.CURRENT;
+            UserHandle userToBind = isManagedProfile ? userFromCall : UserHandle.CURRENT;
+            if ((mInCallServiceInfo.mType == IN_CALL_SERVICE_TYPE_NON_UI
+                    || mInCallServiceInfo.mType == IN_CALL_SERVICE_TYPE_CAR_MODE_UI) && (
+                    mUserHandleToUseForBinding != null)) {
+                //guarding change for non-UI/carmode-UI services which may not be present for
+                // work profile.
+                //In this case, we use the parent user handle. (This also seems to be more
+                // accurate that USER_CURRENT since we queried/discovered the packages using the
+                // parent handle)
+                if (mInCallServiceInfo.hasCrossUserOrProfilePermission()) {
+                    userToBind = mUserHandleToUseForBinding;
+                } else {
+                    Log.i(this,
+                            "service does not have INTERACT_ACROSS_PROFILES or "
+                                    + "INTERACT_ACROSS_USERS permission");
+                }
+            }
+            Log.i(this, "using user id: %s for binding. User from Call is: %s", userToBind,
+                    userFromCall);
             if (!mContext.bindServiceAsUser(intent, mServiceConnection,
                     Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
                         | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
@@ -668,7 +701,7 @@
                 }
 
                 mCarModeConnection = mCurrentConnection =
-                        new InCallServiceBindingConnection(carModeConnectionInfo);
+                        new InCallServiceBindingConnection(carModeConnectionInfo, userHandle);
                 mIsCarMode = true;
 
                 int result = mCurrentConnection.connect(null);
@@ -960,34 +993,137 @@
         }
     };
 
+    private UserHandle findChildManagedProfileUser(UserHandle parent, UserManager um) {
+        UserHandle childManagedProfileUser = null;
+
+        //find child managed profile user (if any)
+        List<UserHandle> allUsers = um.getAllProfiles();
+        for (UserHandle u : allUsers) {
+            if ((um.getProfileParent(u) != null) && (um.getProfileParent(u).equals(parent))
+                    && um.isManagedProfile(u.getIdentifier())) {
+                //found managed profile child
+                Log.i(this,
+                        "Child managed profile user found: " + u.getIdentifier());
+                childManagedProfileUser = u;
+                break;
+            }
+        }
+        return childManagedProfileUser;
+    }
     private BroadcastReceiver mPackageChangedReceiver = new BroadcastReceiver() {
+        private List<InCallController.InCallServiceInfo> getNonUiInCallServiceInfoList(
+                Intent intent, UserHandle userHandle) {
+            String changedPackage = intent.getData().getSchemeSpecificPart();
+            List<InCallController.InCallServiceInfo> inCallServiceInfoList =
+                    Arrays.stream(intent.getStringArrayExtra(
+                                    Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST))
+                            .map((className) ->
+                                    ComponentName.createRelative(changedPackage,
+                                            className))
+                            .filter(mKnownNonUiInCallServices::contains)
+                            .flatMap(componentName -> getInCallServiceComponents(
+                                    userHandle, componentName,
+                                    IN_CALL_SERVICE_TYPE_NON_UI).stream())
+                            .collect(Collectors.toList());
+            return ((inCallServiceInfoList != null) ? inCallServiceInfoList : new ArrayList<>());
+        }
+
+        //Here we query components using the userHandle. We then also query components using the
+        //parent userHandle (if any) while removing duplicates. For non-dup components found using
+        //parent userHandle, we use the overloaded InCallServiceBindingConnection constructor.
+        @SuppressWarnings("ReturnValueIgnored")
+        private List<InCallServiceBindingConnection> getNonUiInCallServiceBindingConnectionList(
+                Intent intent, @NonNull UserHandle userHandle, UserHandle parentUserHandle) {
+            List<InCallServiceBindingConnection> result = new ArrayList<>();
+            List<InCallController.InCallServiceInfo> serviceInfoListForParent = new ArrayList<>();
+
+            //query and add components for the child
+            List<InCallController.InCallServiceInfo> serviceInfoListForUser =
+                    getNonUiInCallServiceInfoList(intent, userHandle);
+
+            //if user has a parent, get components for parents
+            if (parentUserHandle != null) {
+                serviceInfoListForParent = getNonUiInCallServiceInfoList(intent, parentUserHandle);
+            }
+
+            serviceInfoListForUser
+                    .stream()
+                    .map(InCallServiceBindingConnection::new)
+                    .collect(Collectors.toCollection(() -> result));
+
+            serviceInfoListForParent
+                    .stream()
+                    .filter((e) -> !(serviceInfoListForUser.contains(e)))
+                    .map((serviceinfo) -> new InCallServiceBindingConnection(serviceinfo,
+                            parentUserHandle))
+                    .collect(Collectors.toCollection(() -> result));
+
+            return result;
+        }
+
         @Override
         public void onReceive(Context context, Intent intent) {
             Log.startSession("ICC.pCR");
+            UserManager um = mContext.getSystemService(UserManager.class);
             try {
                 if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
                     synchronized (mLock) {
-                        String changedPackage = intent.getData().getSchemeSpecificPart();
                         int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
                         UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
-                        List<InCallServiceBindingConnection> componentsToBind =
-                                Arrays.stream(intent.getStringArrayExtra(
-                                        Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST))
-                                        .map((className) ->
-                                                ComponentName.createRelative(changedPackage,
-                                                        className))
-                                        .filter(mKnownNonUiInCallServices::contains)
-                                        .flatMap(componentName -> getInCallServiceComponents(
-                                                userHandle, componentName,
-                                                IN_CALL_SERVICE_TYPE_NON_UI).stream())
-                                        .map(InCallServiceBindingConnection::new)
-                                        .collect(Collectors.toList());
+                        boolean isManagedProfile = um.isManagedProfile(userHandle.getIdentifier());
 
-                        if (mNonUIInCallServiceConnections.containsKey(userHandle)) {
-                            mNonUIInCallServiceConnections.get(userHandle).
-                                    addConnections(componentsToBind);
+                        /*
+                        There are two possibilities here:
+                         1) We get a work-profile/managed userHandle. In this case we need to check
+                         if there are any ongoing calls for that user. If yes, then process further
+                         by querying component using this user handle (also bindAsUser using this
+                          handle). Else safely ignore it.
+                                OR
+                         2) We get the primary/non-managed userHandle. In this case, we have two
+                          sub-cases to handle:
+                                   a) If there are ongoing calls for this user, query components
+                                   using this user and addConnections
+                                   b) If there are ongoing calls for the child of this user, we
+                                   also addConnections to that child (but invoke bindAsUser later
+                                    with the parent handle).
+
+                         */
+
+                        UserHandle childManagedProfileUser = findChildManagedProfileUser(
+                                userHandle, um);
+                        boolean isUserKeyPresent = mNonUIInCallServiceConnections.containsKey(
+                                userHandle);
+                        boolean isChildUserKeyPresent = (childManagedProfileUser == null) ? false
+                                : mNonUIInCallServiceConnections.containsKey(
+                                        childManagedProfileUser);
+                        List<InCallServiceBindingConnection> componentsToBindForUser = null;
+                        List<InCallServiceBindingConnection> componentsToBindForChild = null;
+
+                        if(isUserKeyPresent) {
+                            componentsToBindForUser =
+                                    getNonUiInCallServiceBindingConnectionList(intent,
+                                            userHandle, null);
+                        }
+                        if (isChildUserKeyPresent) {
+                            componentsToBindForChild =
+                                    getNonUiInCallServiceBindingConnectionList(intent,
+                                            childManagedProfileUser, userHandle);
                         }
 
+                        Log.i(this,
+                                "isUserKeyPresent:%b isChildKeyPresent:%b isManagedProfile:%b "
+                                        + "user:%d",
+                                isUserKeyPresent, isChildUserKeyPresent, isManagedProfile,
+                                userHandle.getIdentifier());
+
+                        if (isUserKeyPresent && componentsToBindForUser != null) {
+                            mNonUIInCallServiceConnections.get(userHandle).
+                                    addConnections(componentsToBindForUser);
+                        }
+                        if (isChildUserKeyPresent && componentsToBindForChild != null) {
+                            mNonUIInCallServiceConnections.get(childManagedProfileUser).
+                                    addConnections(componentsToBindForChild);
+                        }
                         // If the current car mode app become enabled from disabled, update
                         // the connection to binding
                         updateCarModeForConnections();
@@ -1571,6 +1707,11 @@
         return mInCallServices;
     }
 
+    @VisibleForTesting
+    public Map<UserHandle, CarSwappingInCallServiceConnection> getInCallServiceConnections() {
+        return mInCallServiceConnections;
+    }
+
     void silenceRinger(Set<UserHandle> userHandles) {
         userHandles.forEach(userHandle -> {
             if (mInCallServices.containsKey(userHandle)) {
@@ -1691,6 +1832,14 @@
     @VisibleForTesting
     public void bindToServices(Call call) {
         UserHandle userFromCall = getUserFromCall(call);
+        UserHandle parentUser = null;
+        UserManager um = mContext.getSystemService(UserManager.class);
+
+        if (um.isManagedProfile(userFromCall.getIdentifier())) {
+            parentUser = um.getProfileParent(userFromCall);
+            Log.i(this, "child:%s  parent:%s", userFromCall, parentUser);
+        }
+
         if (!mInCallServiceConnections.containsKey(userFromCall)) {
             InCallServiceConnection dialerInCall = null;
             InCallServiceInfo defaultDialerComponentInfo = getDefaultDialerComponent(userFromCall);
@@ -1710,10 +1859,24 @@
 
             InCallServiceConnection carModeInCall = null;
             InCallServiceInfo carModeComponentInfo = getCurrentCarModeComponent(userFromCall);
+            InCallServiceInfo carModeComponentInfoForParentUser = null;
+            if(parentUser != null) {
+                //query using parent user too
+                carModeComponentInfoForParentUser = getCurrentCarModeComponent(
+                        parentUser);
+            }
+
             if (carModeComponentInfo != null &&
                     !carModeComponentInfo.getComponentName().equals(
                             mDefaultDialerCache.getSystemDialerComponent())) {
                 carModeInCall = new InCallServiceBindingConnection(carModeComponentInfo);
+            } else if (carModeComponentInfo == null &&
+                    carModeComponentInfoForParentUser != null &&
+                    !carModeComponentInfoForParentUser.getComponentName().equals(
+                            mDefaultDialerCache.getSystemDialerComponent())) {
+                carModeInCall = new InCallServiceBindingConnection(
+                        carModeComponentInfoForParentUser, parentUser);
+                Log.i(this, "Using car mode component queried using parent handle");
             }
 
             mInCallServiceConnections.put(userFromCall,
@@ -1747,12 +1910,43 @@
 
     private void updateNonUiInCallServices(Call call) {
         UserHandle userFromCall = getUserFromCall(call);
+        UserHandle parentUser = null;
+
+        UserManager um = mContext.getSystemService(UserManager.class);
+        if(um.isManagedProfile(userFromCall.getIdentifier()))
+        {
+            parentUser = um.getProfileParent(userFromCall);
+        }
+
         List<InCallServiceInfo> nonUIInCallComponents =
                 getInCallServiceComponents(userFromCall, IN_CALL_SERVICE_TYPE_NON_UI);
+        List<InCallServiceInfo> nonUIInCallComponentsForParent = new ArrayList<>();
+        if(parentUser != null)
+        {
+            //also get Non-UI services using parent handle.
+            nonUIInCallComponentsForParent =
+                    getInCallServiceComponents(parentUser, IN_CALL_SERVICE_TYPE_NON_UI);
+
+        }
         List<InCallServiceBindingConnection> nonUIInCalls = new LinkedList<>();
         for (InCallServiceInfo serviceInfo : nonUIInCallComponents) {
             nonUIInCalls.add(new InCallServiceBindingConnection(serviceInfo));
         }
+
+        //add nonUI InCall services queried using parent user (if any)
+        for (InCallServiceInfo serviceInfo : nonUIInCallComponentsForParent) {
+            if (nonUIInCallComponents.contains(serviceInfo)) {
+                //skip dups
+                Log.i(this, "skipped duplicate component found using parent user: "
+                        + serviceInfo.getComponentName());
+            } else {
+                nonUIInCalls.add(new InCallServiceBindingConnection(serviceInfo, parentUser));
+                Log.i(this,
+                        "added component queried using parent user: "
+                                + serviceInfo.getComponentName());
+            }
+        }
+
         List<String> callCompanionApps = mCallsManager
                 .getRoleManagerAdapter().getCallCompanionApps();
         if (callCompanionApps != null && !callCompanionApps.isEmpty()) {
@@ -1819,7 +2013,7 @@
             // Last Resort: Try to bind to the ComponentName given directly.
             Log.e(this, new Exception(), "Package Manager could not find ComponentName: "
                     + componentName + ". Trying to bind anyway.");
-            return new InCallServiceInfo(componentName, false, false, type);
+            return new InCallServiceInfo(componentName, false, false, type, false);
         }
     }
 
@@ -1854,6 +2048,34 @@
         return getInCallServiceComponents(userHandle, packageName,
                 componentName, requestedType, true /* ignoreDisabled */);
     }
+    private boolean canInteractAcrossUsersOrProfiles(ServiceInfo serviceInfo,
+            PackageManager packageManager) {
+        String op = AppOpsManager.permissionToOp("android.permission.INTERACT_ACROSS_PROFILES");
+        String[] uidPackages = packageManager.getPackagesForUid(serviceInfo.applicationInfo.uid);
+
+        boolean hasInteractAcrossProfiles = Arrays.stream(uidPackages).anyMatch(
+                p -> ((packageManager.checkPermission(
+                        Manifest.permission.INTERACT_ACROSS_PROFILES,
+                        p) == PackageManager.PERMISSION_GRANTED)
+                ));
+        boolean hasInteractAcrossUsers = Arrays.stream(uidPackages).anyMatch(
+                p -> ((packageManager.checkPermission(
+                        Manifest.permission.INTERACT_ACROSS_USERS,
+                        p) == PackageManager.PERMISSION_GRANTED)
+                ));
+        boolean hasInteractAcrossProfilesAppOp = Arrays.stream(uidPackages).anyMatch(
+                p -> (AppOpsManager.MODE_ALLOWED == mAppOpsManager.checkOpNoThrow(
+                        op, serviceInfo.applicationInfo.uid, p))
+        );
+        Log.i(this,
+                "packageName:%s INTERACT_ACROSS_USERS:%b INTERACT_ACROSS_PROFILES:%b "
+                        + "INTERACT_ACROSS_PROFILES_APPOP:%b",
+                uidPackages[0], hasInteractAcrossUsers, hasInteractAcrossProfiles,
+                hasInteractAcrossProfilesAppOp);
+
+        return (hasInteractAcrossUsers || hasInteractAcrossProfiles
+                || hasInteractAcrossProfilesAppOp);
+    }
 
     private List<InCallServiceInfo> getInCallServiceComponents(UserHandle userHandle,
             String packageName, ComponentName componentName,
@@ -1867,11 +2089,16 @@
         if (componentName != null) {
             serviceIntent.setComponent(componentName);
         }
+        Log.i(this,
+                "getComponents, pkgname: " + packageName + " comp: " + componentName + " userid: "
+                        + userHandle.getIdentifier() + " requestedType: " + requestedType);
         PackageManager packageManager = mContext.getPackageManager();
         Context userContext = mContext.createContextAsUser(userHandle,
                 0 /* flags */);
         PackageManager userPackageManager = userContext != null ?
                 userContext.getPackageManager() : packageManager;
+
+
         for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(
                 serviceIntent,
                 PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS,
@@ -1888,9 +2115,13 @@
 
                 int currentType = getInCallServiceType(userHandle,
                         entry.serviceInfo, packageManager, packageName);
+
+                boolean hasInteractAcrossUserOrProfilePerm = canInteractAcrossUsersOrProfiles(
+                        entry.serviceInfo, packageManager);
+
                 ComponentName foundComponentName =
                         new ComponentName(serviceInfo.packageName, serviceInfo.name);
-                if (requestedType == IN_CALL_SERVICE_TYPE_NON_UI) {
+                if (currentType == IN_CALL_SERVICE_TYPE_NON_UI) {
                     mKnownNonUiInCallServices.add(foundComponentName);
                 }
 
@@ -1903,9 +2134,16 @@
                     isRequestedType = requestedType == currentType;
                 }
 
+                Log.i(this,
+                        "found:%s isRequestedtype:%b isEnabled:%b ignoreDisabled:%b "
+                                + "hasCrossProfilePerm:%b",
+                        foundComponentName, isRequestedType, isEnabled, ignoreDisabled,
+                        hasInteractAcrossUserOrProfilePerm);
+
                 if ((!ignoreDisabled || isEnabled) && isRequestedType) {
                     retval.add(new InCallServiceInfo(foundComponentName, isExternalCallsSupported,
-                            isSelfManageCallsSupported, requestedType));
+                            isSelfManageCallsSupported, requestedType,
+                            hasInteractAcrossUserOrProfilePerm));
                 }
             }
         }
@@ -2439,19 +2677,48 @@
         Log.i(this, "updateCarModeForConnections: car mode apps: %s",
                 mCarModeTracker.getCarModeApps().stream().collect(Collectors.joining(", ")));
 
-        if (mInCallServiceConnections.containsKey(mCallsManager.getCurrentUserHandle())) {
-            CarSwappingInCallServiceConnection inCallServiceConnection = mInCallServiceConnections.
-                    get(mCallsManager.getCurrentUserHandle());
-            if (shouldUseCarModeUI()) {
-                Log.i(this, "updateCarModeForConnections: potentially update car mode app.");
-                inCallServiceConnection.changeCarModeApp(mCarModeTracker.getCurrentCarModePackage(),
-                        mCallsManager.getCurrentUserHandle());
-            } else {
-                if (inCallServiceConnection.isCarMode()) {
-                    Log.i(this, "updateCarModeForConnections: car mode no longer "
-                            + "applicable; disabling");
-                    inCallServiceConnection.disableCarMode();
-                }
+        UserManager um = mContext.getSystemService(UserManager.class);
+        UserHandle currentUser = mCallsManager.getCurrentUserHandle();
+        UserHandle childUser = findChildManagedProfileUser(currentUser, um);
+
+        CarSwappingInCallServiceConnection inCallServiceConnectionForCurrentUser = null;
+        CarSwappingInCallServiceConnection inCallServiceConnectionForChildUser = null;
+
+        Log.i(this, "update carmode current:%s parent:%s", currentUser, childUser);
+        if (mInCallServiceConnections.containsKey(currentUser)) {
+            inCallServiceConnectionForCurrentUser = mInCallServiceConnections.
+                    get(currentUser);
+        }
+        if (childUser != null && mInCallServiceConnections.containsKey(childUser)) {
+            inCallServiceConnectionForChildUser = mInCallServiceConnections.
+                    get(childUser);
+        }
+
+        if (shouldUseCarModeUI()) {
+            Log.i(this, "updateCarModeForConnections: potentially update car mode app.");
+            //always pass current user to changeCarMode. That will ultimately be used for bindAsUser
+            if (inCallServiceConnectionForCurrentUser != null) {
+                inCallServiceConnectionForCurrentUser.changeCarModeApp(
+                        mCarModeTracker.getCurrentCarModePackage(),
+                        currentUser);
+            }
+            if (inCallServiceConnectionForChildUser != null) {
+                inCallServiceConnectionForChildUser.changeCarModeApp(
+                        mCarModeTracker.getCurrentCarModePackage(),
+                        currentUser);
+            }
+        } else {
+            if (inCallServiceConnectionForCurrentUser != null
+                    && inCallServiceConnectionForCurrentUser.isCarMode()) {
+                Log.i(this, "updateCarModeForConnections: car mode no longer "
+                        + "applicable for current user; disabling");
+                inCallServiceConnectionForCurrentUser.disableCarMode();
+            }
+            if (inCallServiceConnectionForChildUser != null
+                    && inCallServiceConnectionForChildUser.isCarMode()) {
+                Log.i(this, "updateCarModeForConnections: car mode no longer "
+                        + "applicable for child user; disabling");
+                inCallServiceConnectionForChildUser.disableCarMode();
             }
         }
     }
@@ -2603,12 +2870,16 @@
         if (call == null) {
             return mCallsManager.getCurrentUserHandle();
         } else {
-            UserHandle userFromCall = call.getUserHandleFromTargetPhoneAccount();
+            UserHandle userFromCall = call.getAssociatedUser();
             UserManager userManager = mContext.getSystemService(UserManager.class);
             // Emergency call should never be blocked, so if the user associated with call is in
             // quite mode, use the primary user for the emergency call.
             if ((call.isEmergencyCall() || call.isInECBM())
-                    && userManager.isQuietModeEnabled(userFromCall)) {
+                    && (userManager.isQuietModeEnabled(userFromCall)
+                    // We should also account for secondary/guest users where the profile may not
+                    // necessarily be paused.
+                    || !userManager.isUserAdmin(mCallsManager.getCurrentUserHandle()
+                    .getIdentifier()))) {
                 return mCallsManager.getCurrentUserHandle();
             }
             return userFromCall;
diff --git a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
index 8426d1f..3b402b1 100644
--- a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
+++ b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
@@ -382,14 +382,14 @@
              * broadcasting.
              */
             callRedirectionWithService = callRedirectionProcessor
-                    .canMakeCallRedirectionWithServiceAsUser(mCall.getInitiatingUser());
+                    .canMakeCallRedirectionWithServiceAsUser(mCall.getAssociatedUser());
             if (callRedirectionWithService) {
-                callRedirectionProcessor.performCallRedirection(mCall.getInitiatingUser());
+                callRedirectionProcessor.performCallRedirection(mCall.getAssociatedUser());
             }
         }
 
         if (disposition.sendBroadcast) {
-            UserHandle targetUser = mCall.getInitiatingUser();
+            UserHandle targetUser = mCall.getAssociatedUser();
             Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser);
             broadcastIntent(mIntent, disposition.number,
                     !disposition.callImmediately && !callRedirectionWithService, targetUser);
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index 9e3a37f..ba5bf2e 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -563,10 +563,7 @@
         if (call == null) {
             return null;
         }
-        UserHandle userHandle = call.getInitiatingUser();
-        if (userHandle == null) {
-            userHandle = call.getTargetPhoneAccount().getUserHandle();
-        }
+        UserHandle userHandle = call.getAssociatedUser();
         PhoneAccountHandle targetPhoneAccount = call.getTargetPhoneAccount();
         Log.d(this, "getSimCallManagerFromCall: callId=%s, targetPhac=%s",
                 call.getId(), targetPhoneAccount);
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 45fb2af..16dc5c4 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -456,7 +456,7 @@
             };
             deferBlockOnRingingFuture = true;  // Run in vibrationLogic.
             if (ringtoneSupplier != null) {
-                mRingtonePlayer.play(ringtoneSupplier, afterRingtoneLogic);
+                mRingtonePlayer.play(ringtoneSupplier, afterRingtoneLogic, isHfpDeviceAttached);
             } else {
                 afterRingtoneLogic.accept(/* ringtone= */ null, /* stopped= */ false);
             }
@@ -552,7 +552,7 @@
         }
 
         if (mInCallController.doesConnectedDialerSupportRinging(
-                call.getUserHandleFromTargetPhoneAccount())) {
+                call.getAssociatedUser())) {
             Log.addEvent(call, LogUtils.Events.SKIP_RINGING, "Dialer handles");
             return;
         }
@@ -689,10 +689,10 @@
         boolean isTheaterModeOn = mSystemSettingsUtil.isTheaterModeOn(mContext);
         timer.record("isTheaterModeOn");
         boolean letDialerHandleRinging = mInCallController.doesConnectedDialerSupportRinging(
-                call.getUserHandleFromTargetPhoneAccount());
+                call.getAssociatedUser());
         timer.record("letDialerHandleRinging");
         boolean isWorkProfileInQuietMode =
-                isProfileInQuietMode(call.getUserHandleFromTargetPhoneAccount());
+                isProfileInQuietMode(call.getAssociatedUser());
         timer.record("isWorkProfileInQuietMode");
 
         Log.i(this, "startRinging timings: " + timer);
diff --git a/src/com/android/server/telecom/RingtoneFactory.java b/src/com/android/server/telecom/RingtoneFactory.java
index 309c86e..6bcfb4c 100644
--- a/src/com/android/server/telecom/RingtoneFactory.java
+++ b/src/com/android/server/telecom/RingtoneFactory.java
@@ -75,7 +75,7 @@
         // or the default ringtone of the receiving user.
         Context userContext = isWorkContact(incomingCall) ?
                 getWorkProfileContextForUser(mCallsManager.getCurrentUserHandle()) :
-                getContextForUserHandle(incomingCall.getUserHandleFromTargetPhoneAccount());
+                getContextForUserHandle(incomingCall.getAssociatedUser());
         Uri ringtoneUri = incomingCall.getRingtone();
         Ringtone ringtone = null;
 
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index ca42b57..7d3eeb6 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -2173,7 +2173,7 @@
                                     || call.getState() == CallState.DISCONNECTING) {
                                 mCallsManager.markCallAsRemoved(call);
                             }
-                            userHandles.add(call.getUserHandleFromTargetPhoneAccount());
+                            userHandles.add(call.getAssociatedUser());
                         }
                         for (UserHandle userHandle : userHandles) {
                             mCallsManager.getInCallController().unbindFromServices(userHandle);
@@ -2707,6 +2707,7 @@
         int packageUid = -1;
         int callingUid = Binder.getCallingUid();
         PackageManager pm;
+        long token = Binder.clearCallingIdentity();
         try{
             pm = mContext.createContextAsUser(
                     UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
@@ -2715,6 +2716,8 @@
             Log.i(this, "callingUidMatchesPackageManagerRecords:"
                             + " createContextAsUser hit exception=[%s]", e.toString());
             return false;
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
         if (pm != null) {
             try {
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index d8e9e8d..3686e86 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -223,6 +223,7 @@
             DeviceIdleControllerAdapter deviceIdleControllerAdapter,
             Ringer.AccessibilityManagerAdapter accessibilityManagerAdapter,
             Executor asyncTaskExecutor,
+            Executor asyncCallAudioTaskExecutor,
             BlockedNumbersAdapter blockedNumbersAdapter) {
         mContext = context.getApplicationContext();
         LogUtils.initLogging(mContext);
@@ -400,6 +401,7 @@
                     callAnomalyWatchdog,
                     accessibilityManagerAdapter,
                     asyncTaskExecutor,
+                    asyncCallAudioTaskExecutor,
                     blockedNumbersAdapter,
                     transactionManager,
                     emergencyCallDiagnosticLogger,
diff --git a/src/com/android/server/telecom/TransactionalServiceWrapper.java b/src/com/android/server/telecom/TransactionalServiceWrapper.java
index 25aaad7..02ccef7 100644
--- a/src/com/android/server/telecom/TransactionalServiceWrapper.java
+++ b/src/com/android/server/telecom/TransactionalServiceWrapper.java
@@ -451,7 +451,8 @@
 
                         @Override
                         public void onError(CallException exception) {
-                            Log.i(TAG, "onSetInactive: onError: with e=[%e]", exception);
+                            Log.w(TAG, "onSetInactive: onError: e.code=[%d], e.msg=[%s]",
+                                    exception.getCode(), exception.getMessage());
                         }
                     });
         } finally {
@@ -498,8 +499,9 @@
 
                         @Override
                         public void onError(CallException exception) {
-                            Log.i(TAG, "onCallStreamingStarted: onError: with e=[%e]",
-                                    exception);
+                            Log.w(TAG, "onCallStreamingStarted: onError: "
+                                            + "e.code=[%d], e.msg=[%s]",
+                                    exception.getCode(), exception.getMessage());
                             stopCallStreaming(call);
                         }
                     }
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index 303f41c..fc55d27 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -27,25 +27,25 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.media.AudioDeviceInfo;
-import android.media.audio.common.AudioDevice;
 import android.os.Bundle;
 import android.telecom.Log;
 import android.util.ArraySet;
 import android.util.LocalLog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.telecom.CallAudioCommunicationDeviceTracker;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.concurrent.Executor;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 public class BluetoothDeviceManager {
 
@@ -177,6 +177,12 @@
             new LinkedHashMap<>();
     private final LinkedHashMap<BluetoothDevice, Integer> mGroupsByDevice =
             new LinkedHashMap<>();
+    private final ArrayList<LinkedHashMap<String, BluetoothDevice>>
+            mDevicesByAddressMaps = new ArrayList<LinkedHashMap<String, BluetoothDevice>>(); {
+        mDevicesByAddressMaps.add(mHfpDevicesByAddress);
+        mDevicesByAddressMaps.add(mHearingAidDevicesByAddress);
+        mDevicesByAddressMaps.add(mLeAudioDevicesByAddress);
+    }
     private int mGroupIdActive = BluetoothLeAudio.GROUP_ID_INVALID;
     private int mGroupIdPending = BluetoothLeAudio.GROUP_ID_INVALID;
     private final LocalLog mLocalLog = new LocalLog(20);
@@ -356,8 +362,10 @@
         }
     }
 
-    void onDeviceConnected(BluetoothDevice device, int deviceType) {
+    @VisibleForTesting
+    public void onDeviceConnected(BluetoothDevice device, int deviceType) {
         synchronized (mLock) {
+            clearDeviceFromDeviceMaps(device.getAddress());
             LinkedHashMap<String, BluetoothDevice> targetDeviceMap;
             if (deviceType == DEVICE_TYPE_LE_AUDIO) {
                 if (mBluetoothLeAudioService == null) {
@@ -401,6 +409,12 @@
         }
     }
 
+    void clearDeviceFromDeviceMaps(String deviceAddress) {
+        for (LinkedHashMap<String, BluetoothDevice> deviceMap : mDevicesByAddressMaps) {
+            deviceMap.remove(deviceAddress);
+        }
+    }
+
     void onDeviceDisconnected(BluetoothDevice device, int deviceType) {
         mLocalLog.log("Device disconnected -- address: " + device.getAddress() + " deviceType: "
                 + deviceType);
@@ -444,13 +458,9 @@
         return mHearingAidSetAsCommunicationDevice;
     }
 
-    public void clearLeAudioCommunicationDevice() {
+    public void clearLeAudioOrSpeakerCommunicationDevice() {
         Log.i(this, "clearLeAudioCommunicationDevice: mLeAudioSetAsCommunicationDevice = " +
                 mLeAudioSetAsCommunicationDevice + " device = " + mLeAudioDevice);
-        if (!mLeAudioSetAsCommunicationDevice) {
-            return;
-        }
-        mLeAudioSetAsCommunicationDevice = false;
         if (mLeAudioDevice != null) {
             mBluetoothRouteManager.onAudioLost(mLeAudioDevice);
             mLeAudioDevice = null;
@@ -462,20 +472,20 @@
         }
 
         AudioDeviceInfo audioDeviceInfo = mAudioManager.getCommunicationDevice();
-        if (audioDeviceInfo != null && audioDeviceInfo.getType()
-                == AudioDeviceInfo.TYPE_BLE_HEADSET) {
-            mBluetoothRouteManager.onAudioLost(audioDeviceInfo.getAddress());
-            mAudioManager.clearCommunicationDevice();
+        if (audioDeviceInfo != null) {
+            if (audioDeviceInfo.getType() == AudioDeviceInfo.TYPE_BLE_HEADSET) {
+                mBluetoothRouteManager.onAudioLost(audioDeviceInfo.getAddress());
+                mAudioManager.clearCommunicationDevice();
+            } else if (audioDeviceInfo.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
+                mAudioManager.clearCommunicationDevice();
+            }
         }
+        mLeAudioSetAsCommunicationDevice = false;
     }
 
-    public void clearHearingAidCommunicationDevice() {
+    public void clearHearingAidOrSpeakerCommunicationDevice() {
         Log.i(this, "clearHearingAidCommunicationDevice: mHearingAidSetAsCommunicationDevice = "
                 + mHearingAidSetAsCommunicationDevice);
-        if (!mHearingAidSetAsCommunicationDevice) {
-            return;
-        }
-        mHearingAidSetAsCommunicationDevice = false;
         if (mHearingAidDevice != null) {
             mBluetoothRouteManager.onAudioLost(mHearingAidDevice);
             mHearingAidDevice = null;
@@ -487,10 +497,15 @@
         }
 
         AudioDeviceInfo audioDeviceInfo = mAudioManager.getCommunicationDevice();
-        if (audioDeviceInfo != null && audioDeviceInfo.getType()
-                == AudioDeviceInfo.TYPE_HEARING_AID) {
-            mAudioManager.clearCommunicationDevice();
+        if (audioDeviceInfo != null) {
+            if (audioDeviceInfo.getType() == AudioDeviceInfo.TYPE_BLE_HEADSET) {
+                mBluetoothRouteManager.onAudioLost(audioDeviceInfo.getAddress());
+                mAudioManager.clearCommunicationDevice();
+            } else if (audioDeviceInfo.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
+                mAudioManager.clearCommunicationDevice();
+            }
         }
+        mHearingAidSetAsCommunicationDevice = false;
     }
 
     public boolean setLeAudioCommunicationDevice() {
@@ -527,7 +542,7 @@
         }
 
         // clear hearing aid communication device if set
-        clearHearingAidCommunicationDevice();
+        clearHearingAidOrSpeakerCommunicationDevice();
 
         // Turn BLE_OUT_HEADSET ON.
         boolean result = mAudioManager.setCommunicationDevice(bleHeadset);
@@ -576,7 +591,7 @@
         }
 
         // clear LE audio communication device if set
-        clearLeAudioCommunicationDevice();
+        clearLeAudioOrSpeakerCommunicationDevice();
 
         // Turn hearing aid ON.
         boolean result = mAudioManager.setCommunicationDevice(hearingAid);
@@ -645,6 +660,7 @@
                     return mCommunicationDeviceTracker.setCommunicationDevice(
                             AudioDeviceInfo.TYPE_BLE_HEADSET, device);
                 }
+                return true;
             }
             return false;
         } else if (callProfile == BluetoothProfile.HEARING_AID) {
@@ -656,6 +672,7 @@
                     return mCommunicationDeviceTracker.setCommunicationDevice(
                             AudioDeviceInfo.TYPE_HEARING_AID, null);
                 }
+                return true;
             }
             return false;
         } else if (callProfile == BluetoothProfile.HEADSET) {
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index 556a8a5..91c03b6 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -80,6 +80,7 @@
         void onBluetoothActiveDevicePresent();
         void onBluetoothActiveDeviceGone();
         void onBluetoothAudioConnected();
+        void onBluetoothAudioConnecting();
         void onBluetoothAudioDisconnected();
         /**
          * This gets called when we get an unexpected state change from Bluetooth. Their stack does
@@ -233,8 +234,7 @@
             sendMessageDelayed(CONNECTION_TIMEOUT, args,
                     mTimeoutsAdapter.getBluetoothPendingTimeoutMillis(
                             mContext.getContentResolver()));
-            // Pretend like audio is connected when communicating w/ CARSM.
-            mListener.onBluetoothAudioConnected();
+            mListener.onBluetoothAudioConnecting();
         }
 
         @Override
diff --git a/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java b/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
index f542fa2..f07c0aa 100644
--- a/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
+++ b/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
@@ -319,7 +319,7 @@
         CallScreeningServiceConnection connection = new CallScreeningServiceConnection(
                 resultFuture);
         if (!CallScreeningServiceHelper.bindCallScreeningService(mContext,
-                mCall.getUserHandleFromTargetPhoneAccount(), mPackageName, connection)) {
+                mCall.getAssociatedUser(), mPackageName, connection)) {
             Log.i(this, "Call screening service binding failed.");
             resultFuture.complete(mPriorStageResult);
         } else {
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index ef85fc7..90a683f 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -215,6 +215,7 @@
                                 }
                             },
                             Executors.newCachedThreadPool(),
+                            Executors.newSingleThreadExecutor(),
                             new BlockedNumbersAdapter() {
                                 @Override
                                 public boolean shouldShowEmergencyCallNotification(Context
diff --git a/src/com/android/server/telecom/ui/AudioProcessingNotification.java b/src/com/android/server/telecom/ui/AudioProcessingNotification.java
index e38178e..952bee8 100644
--- a/src/com/android/server/telecom/ui/AudioProcessingNotification.java
+++ b/src/com/android/server/telecom/ui/AudioProcessingNotification.java
@@ -54,7 +54,7 @@
         } else if (oldState == CallState.AUDIO_PROCESSING
                 && newState != CallState.AUDIO_PROCESSING) {
             cancelAudioProcessingNotification(
-                    call.getUserHandleFromTargetPhoneAccount());
+                    call.getAssociatedUser());
         }
     }
 
@@ -69,7 +69,7 @@
     public void onCallRemoved(Call call) {
         if (call == mCallInAudioProcessing) {
             cancelAudioProcessingNotification(
-                    call.getUserHandleFromTargetPhoneAccount());
+                    call.getAssociatedUser());
         }
     }
 
@@ -80,7 +80,7 @@
      */
     private void showAudioProcessingNotification(Call call) {
         Log.i(this, "showAudioProcessingNotification for user = %s",
-                call.getUserHandleFromTargetPhoneAccount());
+                call.getAssociatedUser());
         mCallInAudioProcessing = call;
 
         Notification.Builder builder = new Notification.Builder(mContext,
@@ -97,7 +97,7 @@
         Notification notification = builder.build();
 
         mNotificationManager.notifyAsUser(NOTIFICATION_TAG, AUDIO_PROCESSING_NOTIFICATION_ID,
-                notification, mCallInAudioProcessing.getUserHandleFromTargetPhoneAccount());
+                notification, mCallInAudioProcessing.getAssociatedUser());
     }
 
     /** Cancels the audio processing notification. */
diff --git a/src/com/android/server/telecom/ui/CallStreamingNotification.java b/src/com/android/server/telecom/ui/CallStreamingNotification.java
index 3f09bb1..8414047 100644
--- a/src/com/android/server/telecom/ui/CallStreamingNotification.java
+++ b/src/com/android/server/telecom/ui/CallStreamingNotification.java
@@ -157,7 +157,7 @@
                 Log.e(this, e, "enqueueStreamingNotification: Couldn't build avatar icon");
             }
             showStreamingNotification(call.getId(),
-                    call.getUserHandleFromTargetPhoneAccount(), call.getCallerDisplayName(),
+                    call.getAssociatedUser(), call.getCallerDisplayName(),
                     call.getHandle(), contactPhotoIcon,
                     call.getTargetPhoneAccount().getComponentName().getPackageName(),
                     call.getConnectTimeMillis());
diff --git a/src/com/android/server/telecom/ui/DisconnectedCallNotifier.java b/src/com/android/server/telecom/ui/DisconnectedCallNotifier.java
index 66f9fe4..1604285 100644
--- a/src/com/android/server/telecom/ui/DisconnectedCallNotifier.java
+++ b/src/com/android/server/telecom/ui/DisconnectedCallNotifier.java
@@ -143,8 +143,7 @@
                 DisconnectCause.REASON_EMERGENCY_CALL_PLACED.equals(cause.getReason())) {
             // Clear any existing notification.
             clearNotification(mCallsManager.getCurrentUserHandle());
-            UserHandle userHandle = call.getTargetPhoneAccount() != null ?
-                    call.getTargetPhoneAccount().getUserHandle() : call.getInitiatingUser();
+            UserHandle userHandle = call.getAssociatedUser();
             // As a last resort, use the current user to display the notification.
             if (userHandle == null) userHandle = mCallsManager.getCurrentUserHandle();
             mPendingCallNotification = new CallInfo(userHandle, call.getHandle(),
diff --git a/src/com/android/server/telecom/ui/IncomingCallNotifier.java b/src/com/android/server/telecom/ui/IncomingCallNotifier.java
index 3b188d4..d419163 100644
--- a/src/com/android/server/telecom/ui/IncomingCallNotifier.java
+++ b/src/com/android/server/telecom/ui/IncomingCallNotifier.java
@@ -168,19 +168,19 @@
             } else if (hadIncomingCall && !hasIncomingCall) {
                 previousIncomingCall.removeListener(mCallListener);
                 hideIncomingCallNotification(
-                        previousIncomingCall.getUserHandleFromTargetPhoneAccount());
+                        previousIncomingCall.getAssociatedUser());
             }
         }
     }
 
     private void showIncomingCallNotification(Call call) {
         Log.i(this, "showIncomingCallNotification showCall = %s for user = %s",
-                call, call.getUserHandleFromTargetPhoneAccount());
+                call, call.getAssociatedUser());
 
         Notification.Builder builder = getNotificationBuilder(call,
                 mCallsManagerProxy.getActiveCall());
         mNotificationManager.notifyAsUser(NOTIFICATION_TAG, NOTIFICATION_INCOMING_CALL,
-                builder.build(), call.getUserHandleFromTargetPhoneAccount());
+                builder.build(), call.getAssociatedUser());
     }
 
     private void hideIncomingCallNotification(UserHandle userHandle) {
diff --git a/src/com/android/server/telecom/voip/VoipCallMonitor.java b/src/com/android/server/telecom/voip/VoipCallMonitor.java
index 3779a6d..8f6ad51 100644
--- a/src/com/android/server/telecom/voip/VoipCallMonitor.java
+++ b/src/com/android/server/telecom/voip/VoipCallMonitor.java
@@ -16,6 +16,12 @@
 
 package com.android.server.telecom.voip;
 
+import static android.app.ForegroundServiceDelegationOptions.DELEGATION_SERVICE_PHONE_CALL;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL;
+
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ForegroundServiceDelegationOptions;
@@ -199,8 +205,11 @@
             ForegroundServiceDelegationOptions options = new ForegroundServiceDelegationOptions(pid,
                     uid, handle.getComponentName().getPackageName(), null /* clientAppThread */,
                     false /* isSticky */, String.valueOf(handle.hashCode()),
-                    0 /* foregroundServiceType */,
-                    ForegroundServiceDelegationOptions.DELEGATION_SERVICE_PHONE_CALL);
+                    FOREGROUND_SERVICE_TYPE_PHONE_CALL |
+                    FOREGROUND_SERVICE_TYPE_MICROPHONE |
+                    FOREGROUND_SERVICE_TYPE_CAMERA |
+                    FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE /* foregroundServiceTypes */,
+                    DELEGATION_SERVICE_PHONE_CALL /* delegationService */);
             ServiceConnection fgsConnection = new ServiceConnection() {
                 @Override
                 public void onServiceConnected(ComponentName name, IBinder service) {
diff --git a/testapps/transactionalVoipApp/res/values-af/strings.xml b/testapps/transactionalVoipApp/res/values-af/strings.xml
index 78abd1b..bf7ad33 100644
--- a/testapps/transactionalVoipApp/res/values-af/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-af/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Luidspreker"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"begin stroom"</string>
+    <string name="crash_app" msgid="2548690390730057704">"gooi uitsondering"</string>
+    <string name="update_notification" msgid="8677916482672588779">"dateer kennisgewing aan voortdurende oproepstyl op"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-am/strings.xml b/testapps/transactionalVoipApp/res/values-am/strings.xml
index 2766bf8..120a9b9 100644
--- a/testapps/transactionalVoipApp/res/values-am/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-am/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"ድምጽ ማውጫ"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ብሉቱዝ"</string>
     <string name="start_stream" msgid="3567634786280097431">"ዥረት ይጀምሩ"</string>
+    <string name="crash_app" msgid="2548690390730057704">"ለየት ያለ ነገርን ይጣሉ"</string>
+    <string name="update_notification" msgid="8677916482672588779">"በመካሄድ ላይ ላለ ጥሪ ቅጥ ማሳወቂያ ያዘምኑ"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ar/strings.xml b/testapps/transactionalVoipApp/res/values-ar/strings.xml
index 8a42e30..d2c1464 100644
--- a/testapps/transactionalVoipApp/res/values-ar/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ar/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"مكبّر الصوت"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"البلوتوث"</string>
     <string name="start_stream" msgid="3567634786280097431">"بدء البث"</string>
+    <string name="crash_app" msgid="2548690390730057704">"طرح استثناء"</string>
+    <string name="update_notification" msgid="8677916482672588779">"إشعار التعديل إلى نمط المكالمات الجارية"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-as/strings.xml b/testapps/transactionalVoipApp/res/values-as/strings.xml
index 56014c4..c48ac0e 100644
--- a/testapps/transactionalVoipApp/res/values-as/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-as/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"স্পীকাৰ"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ব্লুটুথ"</string>
     <string name="start_stream" msgid="3567634786280097431">"ষ্ট্ৰীম কৰিবলৈ আৰম্ভ কৰক"</string>
+    <string name="crash_app" msgid="2548690390730057704">"থ্ৰ’ এক্সচেপশ্বন"</string>
+    <string name="update_notification" msgid="8677916482672588779">"চলিত কলৰ শৈলী সম্পৰ্কে আপডে’ট দিয়া জাননী"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-az/strings.xml b/testapps/transactionalVoipApp/res/values-az/strings.xml
index 14af0ab..75d8278 100644
--- a/testapps/transactionalVoipApp/res/values-az/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-az/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Dinamik"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"yayıma başlayın"</string>
+    <string name="crash_app" msgid="2548690390730057704">"istisna yaradın"</string>
+    <string name="update_notification" msgid="8677916482672588779">"bildirişi davam edən zəng üslubuna yeniləyin"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-b+sr+Latn/strings.xml b/testapps/transactionalVoipApp/res/values-b+sr+Latn/strings.xml
index 3c4019c..f824910 100644
--- a/testapps/transactionalVoipApp/res/values-b+sr+Latn/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-b+sr+Latn/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Zvučnik"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"počnite da strimujete"</string>
+    <string name="crash_app" msgid="2548690390730057704">"izbaciti izuzetak"</string>
+    <string name="update_notification" msgid="8677916482672588779">"ažurirajte obaveštenje na stil aktuelnog poziva"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-be/strings.xml b/testapps/transactionalVoipApp/res/values-be/strings.xml
index 9decf62..36d558e 100644
--- a/testapps/transactionalVoipApp/res/values-be/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-be/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Дынамік"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"пачаць перадачу плынню"</string>
+    <string name="crash_app" msgid="2548690390730057704">"адправіць паведамленне аб выключэнні"</string>
+    <string name="update_notification" msgid="8677916482672588779">"стыль паведамлення аб абнаўленні для бягучага званка"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-bg/strings.xml b/testapps/transactionalVoipApp/res/values-bg/strings.xml
index 63b55f9..2210400 100644
--- a/testapps/transactionalVoipApp/res/values-bg/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-bg/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Високоговорител"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"започване на поточно предаване"</string>
+    <string name="crash_app" msgid="2548690390730057704">"генериране на изключение"</string>
+    <string name="update_notification" msgid="8677916482672588779">"актуализиране на известието до стила на текущото обаждане"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-bn/strings.xml b/testapps/transactionalVoipApp/res/values-bn/strings.xml
index b03123a..45f13be 100644
--- a/testapps/transactionalVoipApp/res/values-bn/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-bn/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"স্পিকার"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ব্লুটুথ"</string>
     <string name="start_stream" msgid="3567634786280097431">"স্ট্রিমিং শুরু করুন"</string>
+    <string name="crash_app" msgid="2548690390730057704">"এক্সেপশন যোগ করুন"</string>
+    <string name="update_notification" msgid="8677916482672588779">"চালু থাকা কলের স্টাইলে আপডেট সংক্রান্ত বিজ্ঞপ্তি"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-bs/strings.xml b/testapps/transactionalVoipApp/res/values-bs/strings.xml
index e4cbb08..24ffba2 100644
--- a/testapps/transactionalVoipApp/res/values-bs/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-bs/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Zvučnik"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"pokreni prijenos"</string>
+    <string name="crash_app" msgid="2548690390730057704">"izbaci izuzetak"</string>
+    <string name="update_notification" msgid="8677916482672588779">"ažuriraj obavještenje u stil poziva u toku"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ca/strings.xml b/testapps/transactionalVoipApp/res/values-ca/strings.xml
index 6780882..06f1655 100644
--- a/testapps/transactionalVoipApp/res/values-ca/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ca/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Altaveu"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"inicia la reproducció en continu"</string>
+    <string name="crash_app" msgid="2548690390730057704">"llança una excepció"</string>
+    <string name="update_notification" msgid="8677916482672588779">"actualitza la notificació a l\'estil de trucada en curs"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-cs/strings.xml b/testapps/transactionalVoipApp/res/values-cs/strings.xml
index 46a938b..6632765 100644
--- a/testapps/transactionalVoipApp/res/values-cs/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-cs/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Reproduktor"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"zahájit streamování"</string>
+    <string name="crash_app" msgid="2548690390730057704">"vyvolat výjimku"</string>
+    <string name="update_notification" msgid="8677916482672588779">"styl aktualizace oznámení o probíhajícím hovoru"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-da/strings.xml b/testapps/transactionalVoipApp/res/values-da/strings.xml
index e857f3e..1a23b58 100644
--- a/testapps/transactionalVoipApp/res/values-da/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-da/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Højttaler"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"start med at streame"</string>
+    <string name="crash_app" msgid="2548690390730057704">"udløs en undtagelse"</string>
+    <string name="update_notification" msgid="8677916482672588779">"opdateringsnotifikation til igangværende opkaldsstil"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-de/strings.xml b/testapps/transactionalVoipApp/res/values-de/strings.xml
index cf3116c..4f853fc 100644
--- a/testapps/transactionalVoipApp/res/values-de/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-de/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Lautsprecher"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"Streaming starten"</string>
+    <string name="crash_app" msgid="2548690390730057704">"Ausnahme auslösen"</string>
+    <string name="update_notification" msgid="8677916482672588779">"Benachrichtigung zum Stil des laufenden Anrufs aktualisieren"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-el/strings.xml b/testapps/transactionalVoipApp/res/values-el/strings.xml
index d838d2e..5553981 100644
--- a/testapps/transactionalVoipApp/res/values-el/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-el/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Ηχείο"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"έναρξη ροής"</string>
+    <string name="crash_app" msgid="2548690390730057704">"εμφάνιση εξαίρεσης"</string>
+    <string name="update_notification" msgid="8677916482672588779">"ενημέρωση ειδοποίησης στο στιλ κλήσης σε εξέλιξη"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-en-rAU/strings.xml b/testapps/transactionalVoipApp/res/values-en-rAU/strings.xml
index 5bfa1a1..bf68cf5 100644
--- a/testapps/transactionalVoipApp/res/values-en-rAU/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-en-rAU/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"start streaming"</string>
+    <string name="crash_app" msgid="2548690390730057704">"throw exception"</string>
+    <string name="update_notification" msgid="8677916482672588779">"Update notification to ongoing call style"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-en-rCA/strings.xml b/testapps/transactionalVoipApp/res/values-en-rCA/strings.xml
index 1014001..269f0d3 100644
--- a/testapps/transactionalVoipApp/res/values-en-rCA/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-en-rCA/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"start streaming"</string>
+    <string name="crash_app" msgid="2548690390730057704">"throw exception"</string>
+    <string name="update_notification" msgid="8677916482672588779">"update notification to ongoing call style"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-en-rGB/strings.xml b/testapps/transactionalVoipApp/res/values-en-rGB/strings.xml
index 5bfa1a1..bf68cf5 100644
--- a/testapps/transactionalVoipApp/res/values-en-rGB/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-en-rGB/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"start streaming"</string>
+    <string name="crash_app" msgid="2548690390730057704">"throw exception"</string>
+    <string name="update_notification" msgid="8677916482672588779">"Update notification to ongoing call style"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-en-rIN/strings.xml b/testapps/transactionalVoipApp/res/values-en-rIN/strings.xml
index 5bfa1a1..bf68cf5 100644
--- a/testapps/transactionalVoipApp/res/values-en-rIN/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-en-rIN/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"start streaming"</string>
+    <string name="crash_app" msgid="2548690390730057704">"throw exception"</string>
+    <string name="update_notification" msgid="8677916482672588779">"Update notification to ongoing call style"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-en-rXC/strings.xml b/testapps/transactionalVoipApp/res/values-en-rXC/strings.xml
index 40b0016..d94683a 100644
--- a/testapps/transactionalVoipApp/res/values-en-rXC/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-en-rXC/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‎‏‏‎‏‏‏‏‎‎‏‎‎‎‎‏‏‎‏‎‎‏‏‏‎‏‎Speaker‎‏‎‎‏‎"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎‏‏‏‎‏‎‎‏‏‏‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‏‏‏‎‎‎Bluetooth‎‏‎‎‏‎"</string>
     <string name="start_stream" msgid="3567634786280097431">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‏‎‏‎‏‎‎‎‏‏‎‏‎‎‏‎‏‏‏‎start streaming‎‏‎‎‏‎"</string>
+    <string name="crash_app" msgid="2548690390730057704">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‎‎‎‏‎‎‎‏‏‎‏‎‎‎‎‎‏‎‎‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‎‎‎throw exception‎‏‎‎‏‎"</string>
+    <string name="update_notification" msgid="8677916482672588779">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‏‎‎‎‏‎‎‏‏‏‎‎‎‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‎‏‏‎update notification to ongoing call style‎‏‎‎‏‎"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-es-rUS/strings.xml b/testapps/transactionalVoipApp/res/values-es-rUS/strings.xml
index 3410a16..da554d1 100644
--- a/testapps/transactionalVoipApp/res/values-es-rUS/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-es-rUS/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Bocina"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"Iniciar transmisión"</string>
+    <string name="crash_app" msgid="2548690390730057704">"generación de excepción"</string>
+    <string name="update_notification" msgid="8677916482672588779">"notificación de actualización del estilo de llamada en curso"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-es/strings.xml b/testapps/transactionalVoipApp/res/values-es/strings.xml
index 2ce1e81..b3f2919 100644
--- a/testapps/transactionalVoipApp/res/values-es/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-es/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Altavoz"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"iniciar emisión"</string>
+    <string name="crash_app" msgid="2548690390730057704">"excepción de expresión \"throw\""</string>
+    <string name="update_notification" msgid="8677916482672588779">"actualizar notificación al estilo de llamada en curso"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-et/strings.xml b/testapps/transactionalVoipApp/res/values-et/strings.xml
index 477dec5..4cc5aab 100644
--- a/testapps/transactionalVoipApp/res/values-et/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-et/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Kõlar"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"käivita voogesitus"</string>
+    <string name="crash_app" msgid="2548690390730057704">"erandi viskamine"</string>
+    <string name="update_notification" msgid="8677916482672588779">"värskendage märguannet käimasoleva kõne stiilis"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-eu/strings.xml b/testapps/transactionalVoipApp/res/values-eu/strings.xml
index 962346f..8b3a181 100644
--- a/testapps/transactionalVoipApp/res/values-eu/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-eu/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Bozgorailua"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetootha"</string>
     <string name="start_stream" msgid="3567634786280097431">"hasi zuzenean igortzen"</string>
+    <string name="crash_app" msgid="2548690390730057704">"eman salbuespena"</string>
+    <string name="update_notification" msgid="8677916482672588779">"eguneratu jakinarazpena, abian den deiaren estiloarekin bat etor dadin"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-fa/strings.xml b/testapps/transactionalVoipApp/res/values-fa/strings.xml
index bd9cddf..88143cb 100644
--- a/testapps/transactionalVoipApp/res/values-fa/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-fa/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"بلندگو"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"بلوتوث"</string>
     <string name="start_stream" msgid="3567634786280097431">"شروع جاری‌سازی"</string>
+    <string name="crash_app" msgid="2548690390730057704">"استثنا قائل شدن"</string>
+    <string name="update_notification" msgid="8677916482672588779">"به‌روزرسانی اعلان به‌سبک تماس درحال انجام"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-fi/strings.xml b/testapps/transactionalVoipApp/res/values-fi/strings.xml
index c95efcb..673d56d 100644
--- a/testapps/transactionalVoipApp/res/values-fi/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-fi/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Kaiutin"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"aloita suoratoisto"</string>
+    <string name="crash_app" msgid="2548690390730057704">"lähetyspoikkeus"</string>
+    <string name="update_notification" msgid="8677916482672588779">"päivitä ilmoitus käynnissä olevan puhelun tyyliin"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-fr-rCA/strings.xml b/testapps/transactionalVoipApp/res/values-fr-rCA/strings.xml
index 64df91c..d58aa13 100644
--- a/testapps/transactionalVoipApp/res/values-fr-rCA/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-fr-rCA/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Haut-parleur"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"démarrer une diffusion"</string>
+    <string name="crash_app" msgid="2548690390730057704">"générer une exception"</string>
+    <string name="update_notification" msgid="8677916482672588779">"modifier la notification en fonction du style de l\'appel en cours"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-fr/strings.xml b/testapps/transactionalVoipApp/res/values-fr/strings.xml
index f1d1bd7..780b8e8 100644
--- a/testapps/transactionalVoipApp/res/values-fr/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-fr/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Haut-parleur"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"démarrer la diffusion"</string>
+    <string name="crash_app" msgid="2548690390730057704">"générer une exception"</string>
+    <string name="update_notification" msgid="8677916482672588779">"modifier la notification en fonction du style de l\'appel en cours"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-gl/strings.xml b/testapps/transactionalVoipApp/res/values-gl/strings.xml
index 76fbb34..f168ab2 100644
--- a/testapps/transactionalVoipApp/res/values-gl/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-gl/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Altofalante"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"iniciar reprodución en tempo real"</string>
+    <string name="crash_app" msgid="2548690390730057704">"activar excepción"</string>
+    <string name="update_notification" msgid="8677916482672588779">"actualiza a notificación en función do estilo da chamada en curso"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-gu/strings.xml b/testapps/transactionalVoipApp/res/values-gu/strings.xml
index b0066da..60bb0b7 100644
--- a/testapps/transactionalVoipApp/res/values-gu/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-gu/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"સ્પીકર"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"બ્લૂટૂથ"</string>
     <string name="start_stream" msgid="3567634786280097431">"સ્ટ્રીમિંગ શરૂ કરો"</string>
+    <string name="crash_app" msgid="2548690390730057704">"અપવાદ થ્રો કરો"</string>
+    <string name="update_notification" msgid="8677916482672588779">"ચાલુ કૉલ શૈલી પર નોટિફિકેશન અપડેટ કરો"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-hi/strings.xml b/testapps/transactionalVoipApp/res/values-hi/strings.xml
index a6e4a10..ba4262a 100644
--- a/testapps/transactionalVoipApp/res/values-hi/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-hi/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"स्पीकर"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ब्लूटूथ"</string>
     <string name="start_stream" msgid="3567634786280097431">"स्ट्रीमिंग शुरू करें"</string>
+    <string name="crash_app" msgid="2548690390730057704">"अपवाद जोड़ें"</string>
+    <string name="update_notification" msgid="8677916482672588779">"मौजूदा कॉल की स्टाइल के हिसाब से सूचनाओं को अपडेट करें"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-hr/strings.xml b/testapps/transactionalVoipApp/res/values-hr/strings.xml
index 768d378..c324f6d 100644
--- a/testapps/transactionalVoipApp/res/values-hr/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-hr/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Zvučnik"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"pokretanje streaminga"</string>
+    <string name="crash_app" msgid="2548690390730057704">"izbacivanje iznimke"</string>
+    <string name="update_notification" msgid="8677916482672588779">"ažuriranje obavijesti u stil poziva u tijeku"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-hu/strings.xml b/testapps/transactionalVoipApp/res/values-hu/strings.xml
index cda3b7e..205404e 100644
--- a/testapps/transactionalVoipApp/res/values-hu/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-hu/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Hangszóró"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"streamelés indítása"</string>
+    <string name="crash_app" msgid="2548690390730057704">"kivétel dobása"</string>
+    <string name="update_notification" msgid="8677916482672588779">"értesítés frissítése a folyamatban lévő hívás stílusára"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-hy/strings.xml b/testapps/transactionalVoipApp/res/values-hy/strings.xml
index b56941f..85e6ae5 100644
--- a/testapps/transactionalVoipApp/res/values-hy/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-hy/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Բարձրախոս"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"սկսել հեռարձակում"</string>
+    <string name="crash_app" msgid="2548690390730057704">"ուղարկել հաղորդագրություն բացառության մասին"</string>
+    <string name="update_notification" msgid="8677916482672588779">"ծանուցում ընթացիկ զանգի ոճի մասին"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-in/strings.xml b/testapps/transactionalVoipApp/res/values-in/strings.xml
index e29fea7..935f036 100644
--- a/testapps/transactionalVoipApp/res/values-in/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-in/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"mulai streaming"</string>
+    <string name="crash_app" msgid="2548690390730057704">"tampilkan pengecualian"</string>
+    <string name="update_notification" msgid="8677916482672588779">"perbarui notifikasi ke gaya panggilan yang sedang berlangsung"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-is/strings.xml b/testapps/transactionalVoipApp/res/values-is/strings.xml
index 4ecb2ca..c0bcd23 100644
--- a/testapps/transactionalVoipApp/res/values-is/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-is/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Hátalari"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"hefja streymi"</string>
+    <string name="crash_app" msgid="2548690390730057704">"nota undantekningu"</string>
+    <string name="update_notification" msgid="8677916482672588779">"uppfæra tilkynningu í stíl símtals sem stendur yfir"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-it/strings.xml b/testapps/transactionalVoipApp/res/values-it/strings.xml
index bb83aa1..36a2816 100644
--- a/testapps/transactionalVoipApp/res/values-it/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-it/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Altoparlante"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"avvia streaming"</string>
+    <string name="crash_app" msgid="2548690390730057704">"genera eccezione"</string>
+    <string name="update_notification" msgid="8677916482672588779">"aggiorna la notifica allo stile di chiamata in corso"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-iw/strings.xml b/testapps/transactionalVoipApp/res/values-iw/strings.xml
index 4de997e..3accc06 100644
--- a/testapps/transactionalVoipApp/res/values-iw/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-iw/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"רמקול"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"התחלת השידור"</string>
+    <string name="crash_app" msgid="2548690390730057704">"חריגה להקפצה של הודעת שגיאה"</string>
+    <string name="update_notification" msgid="8677916482672588779">"עדכון ההתראה לסגנון של שיחה רציפה"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ja/strings.xml b/testapps/transactionalVoipApp/res/values-ja/strings.xml
index a5e8251..faaede6 100644
--- a/testapps/transactionalVoipApp/res/values-ja/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ja/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"スピーカー"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"ストリーミングを開始"</string>
+    <string name="crash_app" msgid="2548690390730057704">"例外をスロー"</string>
+    <string name="update_notification" msgid="8677916482672588779">"通話中スタイルへの通知を更新"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ka/strings.xml b/testapps/transactionalVoipApp/res/values-ka/strings.xml
index 671cffb..6d94f3e 100644
--- a/testapps/transactionalVoipApp/res/values-ka/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ka/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"დინამიკი"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"სტრიმინგის დაწყება"</string>
+    <string name="crash_app" msgid="2548690390730057704">"ხარვეზის გადასროლა"</string>
+    <string name="update_notification" msgid="8677916482672588779">"განაახლეთ შეტყობინება მიმდინარე ზარის სტილში"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-kk/strings.xml b/testapps/transactionalVoipApp/res/values-kk/strings.xml
index 2713491..03fd031 100644
--- a/testapps/transactionalVoipApp/res/values-kk/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-kk/strings.xml
@@ -28,8 +28,10 @@
     <string name="answer" msgid="5423590397665409939">"жауап беру"</string>
     <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
     <string name="disconnect_call" msgid="1349412380315371385">"ажырату"</string>
-    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Динамик"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Телефон динамигі"</string>
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Динамик"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"трансляцияны бастау"</string>
+    <string name="crash_app" msgid="2548690390730057704">"ерекше жағдай туралы хабарлау"</string>
+    <string name="update_notification" msgid="8677916482672588779">"жүріп жатқан қоңырау стиліндегі хабарландыруды жаңату"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-km/strings.xml b/testapps/transactionalVoipApp/res/values-km/strings.xml
index 13f4983..b3e45e4 100644
--- a/testapps/transactionalVoipApp/res/values-km/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-km/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"ឧបករណ៍​បំពង​សំឡេង"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ប៊្លូធូស"</string>
     <string name="start_stream" msgid="3567634786280097431">"ចាប់ផ្ដើម​ការផ្សាយ"</string>
+    <string name="crash_app" msgid="2548690390730057704">"បោះ​ការលើកលែង"</string>
+    <string name="update_notification" msgid="8677916482672588779">"ធ្វើបច្ចុប្បន្នភាព​ការជូនដំណឹង​ចំពោះ​រចនាប័ទ្ម​នៃការហៅ​ទូរសព្ទ​ដែល​កំពុង​ដំណើរការ"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-kn/strings.xml b/testapps/transactionalVoipApp/res/values-kn/strings.xml
index b994f92..dd3fdd9 100644
--- a/testapps/transactionalVoipApp/res/values-kn/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-kn/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"ಸ್ಪೀಕರ್"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ಬ್ಲೂಟೂತ್"</string>
     <string name="start_stream" msgid="3567634786280097431">"ಸ್ಟ್ರೀಮ್ ಮಾಡುವುದನ್ನು ಪ್ರಾರಂಭಿಸಿ"</string>
+    <string name="crash_app" msgid="2548690390730057704">"ಥ್ರೋ ಎಕ್ಸೆಪ್ಶನ್"</string>
+    <string name="update_notification" msgid="8677916482672588779">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಕರೆ ಶೈಲಿಗೆ ನೋಟಿಫಿಕೇಶನ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಿ"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ko/strings.xml b/testapps/transactionalVoipApp/res/values-ko/strings.xml
index 9eb4556..762dc9c 100644
--- a/testapps/transactionalVoipApp/res/values-ko/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ko/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"스피커"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"블루투스"</string>
     <string name="start_stream" msgid="3567634786280097431">"스트리밍 시작"</string>
+    <string name="crash_app" msgid="2548690390730057704">"예외 발생"</string>
+    <string name="update_notification" msgid="8677916482672588779">"진행 중인 통화 스타일로 알림 업데이트"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ky/strings.xml b/testapps/transactionalVoipApp/res/values-ky/strings.xml
index 577dcda..47422a0 100644
--- a/testapps/transactionalVoipApp/res/values-ky/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ky/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Динамик"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"агымды баштоо"</string>
+    <string name="crash_app" msgid="2548690390730057704">"өзгөчө учурду түзүү"</string>
+    <string name="update_notification" msgid="8677916482672588779">"учурдагы чалуу үчүн жаңыртуу тууралуу билдирменин стили"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-lo/strings.xml b/testapps/transactionalVoipApp/res/values-lo/strings.xml
index 69126d9..1e1d247 100644
--- a/testapps/transactionalVoipApp/res/values-lo/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-lo/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"ລຳໂພງ"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"ເລີ່ມການສະຕຣີມ"</string>
+    <string name="crash_app" msgid="2548690390730057704">"ຂໍ້ຍົກເວັ້ນໃນການໂຍນ"</string>
+    <string name="update_notification" msgid="8677916482672588779">"ອັບເດດການແຈ້ງເຕືອນເປັນຮູບແບບການໂທທີ່ກຳລັງດຳເນີນການຢູ່"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-lt/strings.xml b/testapps/transactionalVoipApp/res/values-lt/strings.xml
index 91e51fe..88cd414 100644
--- a/testapps/transactionalVoipApp/res/values-lt/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-lt/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Garsiakalbis"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"pradėti srautinį perdavimą"</string>
+    <string name="crash_app" msgid="2548690390730057704">"siųsti pranešimą apie išimtį"</string>
+    <string name="update_notification" msgid="8677916482672588779">"atnaujinti pranešimą į vykstančio skambučio stilių"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-lv/strings.xml b/testapps/transactionalVoipApp/res/values-lv/strings.xml
index ae6896f..5e91ffe 100644
--- a/testapps/transactionalVoipApp/res/values-lv/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-lv/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Skaļrunis"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"sākt straumēšanu"</string>
+    <string name="crash_app" msgid="2548690390730057704">"sūtīt ziņojumu par izņēmumu"</string>
+    <string name="update_notification" msgid="8677916482672588779">"atjaunināt paziņojumu atbilstoši pašreizējā zvana stilam"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-mk/strings.xml b/testapps/transactionalVoipApp/res/values-mk/strings.xml
index 8501eaf..d86879d 100644
--- a/testapps/transactionalVoipApp/res/values-mk/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-mk/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Звучник"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"започни стриминг"</string>
+    <string name="crash_app" msgid="2548690390730057704">"отфрли исклучок"</string>
+    <string name="update_notification" msgid="8677916482672588779">"известување за ажурирање на стилот на тековниот повик"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ml/strings.xml b/testapps/transactionalVoipApp/res/values-ml/strings.xml
index 67e4e34..6c70b22 100644
--- a/testapps/transactionalVoipApp/res/values-ml/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ml/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"സ്പീക്കർ"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"സ്‌ട്രീമിംഗ് ആരംഭിക്കുക"</string>
+    <string name="crash_app" msgid="2548690390730057704">"ഒഴിവാക്കൽ ത്രോ ചെയ്യുക"</string>
+    <string name="update_notification" msgid="8677916482672588779">"സജീവമായ കോൾ ശൈലിയിലേക്ക് അറിയിപ്പ് അപ്ഡേറ്റ് ചെയ്യുക"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-mn/strings.xml b/testapps/transactionalVoipApp/res/values-mn/strings.xml
index e4b6f36..fecb956 100644
--- a/testapps/transactionalVoipApp/res/values-mn/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-mn/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Чанга яригч"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"дамжуулалтыг эхлүүлэх"</string>
+    <string name="crash_app" msgid="2548690390730057704">"шидэх гажиг"</string>
+    <string name="update_notification" msgid="8677916482672588779">"үргэлжилж буй дуудлагын загварын шинэчлэлтийн мэдэгдэл"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-mr/strings.xml b/testapps/transactionalVoipApp/res/values-mr/strings.xml
index dfb3184..97bf665 100644
--- a/testapps/transactionalVoipApp/res/values-mr/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-mr/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"स्पीकर"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ब्लूटूथ"</string>
     <string name="start_stream" msgid="3567634786280097431">"स्ट्रीम करणे सुरू करा"</string>
+    <string name="crash_app" msgid="2548690390730057704">"एक्सेप्शन जोडा"</string>
+    <string name="update_notification" msgid="8677916482672588779">"सुरू असलेल्या कॉल शैलीवर सूचना अपडेट करा"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ms/strings.xml b/testapps/transactionalVoipApp/res/values-ms/strings.xml
index 3005391..abcb702 100644
--- a/testapps/transactionalVoipApp/res/values-ms/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ms/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Pembesar suara"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"mulakan penstriman"</string>
+    <string name="crash_app" msgid="2548690390730057704">"buat pengecualian"</string>
+    <string name="update_notification" msgid="8677916482672588779">"kemas kinikan pemberitahuan kepada gaya panggilan keluar"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-my/strings.xml b/testapps/transactionalVoipApp/res/values-my/strings.xml
index 818a3f7..b8ee395 100644
--- a/testapps/transactionalVoipApp/res/values-my/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-my/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"စပီကာ"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ဘလူးတုသ်"</string>
     <string name="start_stream" msgid="3567634786280097431">"တိုက်ရိုက်လွှင့်ခြင်း စတင်ရန်"</string>
+    <string name="crash_app" msgid="2548690390730057704">"throw exception"</string>
+    <string name="update_notification" msgid="8677916482672588779">"လက်ရှိခေါ်ဆိုမှုပုံစံအတွက် အပ်ဒိတ်အကြောင်းကြားချက်"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-nb/strings.xml b/testapps/transactionalVoipApp/res/values-nb/strings.xml
index ab0353d..22bb06f 100644
--- a/testapps/transactionalVoipApp/res/values-nb/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-nb/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Høyttaler"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"start strømming"</string>
+    <string name="crash_app" msgid="2548690390730057704">"unntak – avbryt med en feil"</string>
+    <string name="update_notification" msgid="8677916482672588779">"oppdater varslingsstil til «Pågående anrop»"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ne/strings.xml b/testapps/transactionalVoipApp/res/values-ne/strings.xml
index 3a12a70..e9bc805 100644
--- a/testapps/transactionalVoipApp/res/values-ne/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ne/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"स्पिकर"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ब्लुटुथ"</string>
     <string name="start_stream" msgid="3567634786280097431">"स्ट्रिम गर्न थाल्नुहोस्"</string>
+    <string name="crash_app" msgid="2548690390730057704">"अपवाद देखाउने काम"</string>
+    <string name="update_notification" msgid="8677916482672588779">"कल गरिरहेका बेला सूचना जुन शैलीमा देखिन्छ सोही शैली प्रयोग गर्नुहोस्"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-nl/strings.xml b/testapps/transactionalVoipApp/res/values-nl/strings.xml
index 7c9ce32..1ba3f9c 100644
--- a/testapps/transactionalVoipApp/res/values-nl/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-nl/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"streamen starten"</string>
+    <string name="crash_app" msgid="2548690390730057704">"uitzondering activeren"</string>
+    <string name="update_notification" msgid="8677916482672588779">"updatemelding naar actieve gespreksstijl"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-or/strings.xml b/testapps/transactionalVoipApp/res/values-or/strings.xml
index 7a805f4..f3391ea 100644
--- a/testapps/transactionalVoipApp/res/values-or/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-or/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"ସ୍ପିକର"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ବ୍ଲୁଟୁଥ"</string>
     <string name="start_stream" msgid="3567634786280097431">"ଷ୍ଟ୍ରିମିଂ ଆରମ୍ଭ କରନ୍ତୁ"</string>
+    <string name="crash_app" msgid="2548690390730057704">"ଥ୍ରୋ ଏକ୍ସସେପସନ"</string>
+    <string name="update_notification" msgid="8677916482672588779">"ଚାଲିଥିବା କଲ ଷ୍ଟାଇଲ ପାଇଁ ବିଜ୍ଞପ୍ତିକୁ ଅପଡେଟ କରନ୍ତୁ"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-pa/strings.xml b/testapps/transactionalVoipApp/res/values-pa/strings.xml
index 8293899..76e367d 100644
--- a/testapps/transactionalVoipApp/res/values-pa/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-pa/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"ਸਪੀਕਰ"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ਬਲੂਟੁੱਥ"</string>
     <string name="start_stream" msgid="3567634786280097431">"ਸਟ੍ਰੀਮਿੰਗ ਸ਼ੁਰੂ ਕਰੋ"</string>
+    <string name="crash_app" msgid="2548690390730057704">"ਅਪਵਾਦ ਸ਼ਾਮਲ ਕਰੋ"</string>
+    <string name="update_notification" msgid="8677916482672588779">"ਜਾਰੀ ਕਾਲ ਸਟਾਈਲ \'ਤੇ ਸੂਚਨਾ ਅੱਪਡੇਟ ਕਰੋ"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-pl/strings.xml b/testapps/transactionalVoipApp/res/values-pl/strings.xml
index 3cb8ac4..c6115b8 100644
--- a/testapps/transactionalVoipApp/res/values-pl/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-pl/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Głośnik"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"rozpocznij transmisję"</string>
+    <string name="crash_app" msgid="2548690390730057704">"wyjątek dotyczący zgłoszenia"</string>
+    <string name="update_notification" msgid="8677916482672588779">"zaktualizuj powiadomienie do stylu trwającej rozmowy"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-pt-rPT/strings.xml b/testapps/transactionalVoipApp/res/values-pt-rPT/strings.xml
index 6c4f149..a5b3ea0 100644
--- a/testapps/transactionalVoipApp/res/values-pt-rPT/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-pt-rPT/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Altifalante"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"Iniciar stream"</string>
+    <string name="crash_app" msgid="2548690390730057704">"acionar exceção"</string>
+    <string name="update_notification" msgid="8677916482672588779">"atualizar estilo de notificação para chamada em curso"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-pt/strings.xml b/testapps/transactionalVoipApp/res/values-pt/strings.xml
index 97bba50..a09c64d 100644
--- a/testapps/transactionalVoipApp/res/values-pt/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-pt/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Alto-falante"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"Iniciar transmissão"</string>
+    <string name="crash_app" msgid="2548690390730057704">"gerar exceção"</string>
+    <string name="update_notification" msgid="8677916482672588779">"notificação de atualização para o estilo \"Chamada em andamento\""</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ro/strings.xml b/testapps/transactionalVoipApp/res/values-ro/strings.xml
index bb630a8..261a5ad 100644
--- a/testapps/transactionalVoipApp/res/values-ro/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ro/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Difuzor"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"începe streamingul"</string>
+    <string name="crash_app" msgid="2548690390730057704">"trimite excepție"</string>
+    <string name="update_notification" msgid="8677916482672588779">"actualizează notificarea la stilul de apel în desfășurare"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ru/strings.xml b/testapps/transactionalVoipApp/res/values-ru/strings.xml
index 87c06f1..c05e7ea 100644
--- a/testapps/transactionalVoipApp/res/values-ru/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ru/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Колонка"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"Начать трансляцию"</string>
+    <string name="crash_app" msgid="2548690390730057704">"отправить сообщение об исключении"</string>
+    <string name="update_notification" msgid="8677916482672588779">"стиль уведомления об обновлении для текущего звонка"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-si/strings.xml b/testapps/transactionalVoipApp/res/values-si/strings.xml
index c28e166..d8b8a6f 100644
--- a/testapps/transactionalVoipApp/res/values-si/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-si/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"ස්පීකරය"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"බ්ලූටූත්"</string>
     <string name="start_stream" msgid="3567634786280097431">"ප්‍රවාහය අරඹන්න"</string>
+    <string name="crash_app" msgid="2548690390730057704">"ව්‍යතිරේකය දමන්න"</string>
+    <string name="update_notification" msgid="8677916482672588779">"පවතින ඇමතුම් විලාසයට යාවත්කාලීනයේ දැනුම්දීම"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-sk/strings.xml b/testapps/transactionalVoipApp/res/values-sk/strings.xml
index 5e76289..3847882 100644
--- a/testapps/transactionalVoipApp/res/values-sk/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-sk/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Reproduktor"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"spustiť streamovanie"</string>
+    <string name="crash_app" msgid="2548690390730057704">"vyvolať výnimku"</string>
+    <string name="update_notification" msgid="8677916482672588779">"aktualizovať upozornenie na štýl prebiehajúceho hovoru"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-sl/strings.xml b/testapps/transactionalVoipApp/res/values-sl/strings.xml
index 435eac9..dec3622 100644
--- a/testapps/transactionalVoipApp/res/values-sl/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-sl/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Zvočnik"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"začni pretočno predvajanje"</string>
+    <string name="crash_app" msgid="2548690390730057704">"sprožitev izjeme"</string>
+    <string name="update_notification" msgid="8677916482672588779">"posodobi obvestilo na slog trenutnega klica"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-sq/strings.xml b/testapps/transactionalVoipApp/res/values-sq/strings.xml
index 3d18edf..ddaba66 100644
--- a/testapps/transactionalVoipApp/res/values-sq/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-sq/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Altoparlanti"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"nis transmetimin"</string>
+    <string name="crash_app" msgid="2548690390730057704">"gjenero një përjashtim"</string>
+    <string name="update_notification" msgid="8677916482672588779">"përditëso njoftimin me stilin e telefonatës në vazhdim"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-sr/strings.xml b/testapps/transactionalVoipApp/res/values-sr/strings.xml
index df6a08b..cd413f4 100644
--- a/testapps/transactionalVoipApp/res/values-sr/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-sr/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Звучник"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"почните да стримујете"</string>
+    <string name="crash_app" msgid="2548690390730057704">"избацити изузетак"</string>
+    <string name="update_notification" msgid="8677916482672588779">"ажурирајте обавештење на стил актуелног позива"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-sv/strings.xml b/testapps/transactionalVoipApp/res/values-sv/strings.xml
index 51d300a..f74b775 100644
--- a/testapps/transactionalVoipApp/res/values-sv/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-sv/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Högtalare"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"starta streaming"</string>
+    <string name="crash_app" msgid="2548690390730057704">"utlös undantag"</string>
+    <string name="update_notification" msgid="8677916482672588779">"uppdatera avisering till format för pågående samtal"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-sw/strings.xml b/testapps/transactionalVoipApp/res/values-sw/strings.xml
index 3ad2501..b7d0d0f 100644
--- a/testapps/transactionalVoipApp/res/values-sw/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-sw/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Spika"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"anzisha kutiririsha"</string>
+    <string name="crash_app" msgid="2548690390730057704">"hitilafu wakati wa kutekeleza programu"</string>
+    <string name="update_notification" msgid="8677916482672588779">"sasisha arifa kwenye mtindo wa simu inayoendelea"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ta/strings.xml b/testapps/transactionalVoipApp/res/values-ta/strings.xml
index 884291d..39b410a 100644
--- a/testapps/transactionalVoipApp/res/values-ta/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ta/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"ஸ்பீக்கர்"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"புளூடூத்"</string>
     <string name="start_stream" msgid="3567634786280097431">"ஸ்ட்ரீமிங்கைத் தொடங்கு"</string>
+    <string name="crash_app" msgid="2548690390730057704">"விதிவிலக்கைத் தொடங்கு"</string>
+    <string name="update_notification" msgid="8677916482672588779">"செயலில் உள்ள அழைப்பு ஸ்டைலுக்கான அறிவிப்பைப் புதுப்பிக்கவும்"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-te/strings.xml b/testapps/transactionalVoipApp/res/values-te/strings.xml
index b926d1a..f4560ab 100644
--- a/testapps/transactionalVoipApp/res/values-te/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-te/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"స్పీకర్"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"బ్లూటూత్"</string>
     <string name="start_stream" msgid="3567634786280097431">"స్ట్రీమింగ్‌ను ప్రారంభించండి"</string>
+    <string name="crash_app" msgid="2548690390730057704">"మినహాయింపు వేయండి"</string>
+    <string name="update_notification" msgid="8677916482672588779">"జరుగుతున్న కాల్ స్టయిల్‌కి నోటిఫికేషన్‌ను అప్‌డేట్ చేయండి"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-th/strings.xml b/testapps/transactionalVoipApp/res/values-th/strings.xml
index a1a9803..545110b 100644
--- a/testapps/transactionalVoipApp/res/values-th/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-th/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"ลำโพง"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"บลูทูธ"</string>
     <string name="start_stream" msgid="3567634786280097431">"เริ่มสตรีมมิง"</string>
+    <string name="crash_app" msgid="2548690390730057704">"ส่งข้อยกเว้น"</string>
+    <string name="update_notification" msgid="8677916482672588779">"อัปเดตการแจ้งเตือนไปยังรูปแบบการโทรที่ดำเนินอยู่"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-tl/strings.xml b/testapps/transactionalVoipApp/res/values-tl/strings.xml
index d3399ff..6cc2a2b 100644
--- a/testapps/transactionalVoipApp/res/values-tl/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-tl/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"simulan ang streaming"</string>
+    <string name="crash_app" msgid="2548690390730057704">"throw exception"</string>
+    <string name="update_notification" msgid="8677916482672588779">"i-update ang notification sa istilo ng kasalukuyang tawag"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-tr/strings.xml b/testapps/transactionalVoipApp/res/values-tr/strings.xml
index d9a94ab..ec23048 100644
--- a/testapps/transactionalVoipApp/res/values-tr/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-tr/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Hoparlör"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"yayın başlat"</string>
+    <string name="crash_app" msgid="2548690390730057704">"istisna gönder"</string>
+    <string name="update_notification" msgid="8677916482672588779">"bildirimi devam eden arama stiline güncelle"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-uk/strings.xml b/testapps/transactionalVoipApp/res/values-uk/strings.xml
index e08728c..0069f3d 100644
--- a/testapps/transactionalVoipApp/res/values-uk/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-uk/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Колонка"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"Почати трансляцію"</string>
+    <string name="crash_app" msgid="2548690390730057704">"надіслати повідомлення про виняток"</string>
+    <string name="update_notification" msgid="8677916482672588779">"стиль сповіщення про оновлення для поточного дзвінка"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ur/strings.xml b/testapps/transactionalVoipApp/res/values-ur/strings.xml
index e0e0c6e..e41027a 100644
--- a/testapps/transactionalVoipApp/res/values-ur/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ur/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"اسپیکر"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"بلوٹوتھ"</string>
     <string name="start_stream" msgid="3567634786280097431">"سلسلہ بندی شروع کریں"</string>
+    <string name="crash_app" msgid="2548690390730057704">"تھرو ایکسیپشن"</string>
+    <string name="update_notification" msgid="8677916482672588779">"اطلاع کو جاری کال طرز پر اپ ڈیٹ کریں"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-uz/strings.xml b/testapps/transactionalVoipApp/res/values-uz/strings.xml
index 5421322..faa0b4b 100644
--- a/testapps/transactionalVoipApp/res/values-uz/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-uz/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Karnay"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"strimingni boshlash"</string>
+    <string name="crash_app" msgid="2548690390730057704">"istisno berish"</string>
+    <string name="update_notification" msgid="8677916482672588779">"bildirishnomani joriy chaqiruv uslubida yangilash"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-vi/strings.xml b/testapps/transactionalVoipApp/res/values-vi/strings.xml
index 88362e4..a54d544 100644
--- a/testapps/transactionalVoipApp/res/values-vi/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-vi/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Loa"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"bắt đầu phát trực tuyến"</string>
+    <string name="crash_app" msgid="2548690390730057704">"đưa ra trường hợp ngoại lệ"</string>
+    <string name="update_notification" msgid="8677916482672588779">"cập nhật thông báo thành kiểu cuộc gọi đang diễn ra"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-zh-rCN/strings.xml b/testapps/transactionalVoipApp/res/values-zh-rCN/strings.xml
index 4b816ba..a434e35 100644
--- a/testapps/transactionalVoipApp/res/values-zh-rCN/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-zh-rCN/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"扬声器"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"蓝牙"</string>
     <string name="start_stream" msgid="3567634786280097431">"开始直播"</string>
+    <string name="crash_app" msgid="2548690390730057704">"抛出异常"</string>
+    <string name="update_notification" msgid="8677916482672588779">"将通知更新为当前通话样式"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-zh-rHK/strings.xml b/testapps/transactionalVoipApp/res/values-zh-rHK/strings.xml
index 5b80831..e00caa9 100644
--- a/testapps/transactionalVoipApp/res/values-zh-rHK/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-zh-rHK/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"喇叭"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"藍牙"</string>
     <string name="start_stream" msgid="3567634786280097431">"開始串流播放"</string>
+    <string name="crash_app" msgid="2548690390730057704">"擲回例外狀況"</string>
+    <string name="update_notification" msgid="8677916482672588779">"更新通知至通話中樣式"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-zh-rTW/strings.xml b/testapps/transactionalVoipApp/res/values-zh-rTW/strings.xml
index b8a2045..1a6da94 100644
--- a/testapps/transactionalVoipApp/res/values-zh-rTW/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-zh-rTW/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"喇叭"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"藍牙"</string>
     <string name="start_stream" msgid="3567634786280097431">"開始串流播放"</string>
+    <string name="crash_app" msgid="2548690390730057704">"擲回例外狀況"</string>
+    <string name="update_notification" msgid="8677916482672588779">"將通知更新為通話中樣式"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-zu/strings.xml b/testapps/transactionalVoipApp/res/values-zu/strings.xml
index 8e14895..cd86e81 100644
--- a/testapps/transactionalVoipApp/res/values-zu/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-zu/strings.xml
@@ -32,4 +32,6 @@
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Isipikha"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"I-Bluetooth"</string>
     <string name="start_stream" msgid="3567634786280097431">"Qala ukusakaza-bukhoma"</string>
+    <string name="crash_app" msgid="2548690390730057704">"phonsela okuhlukile"</string>
+    <string name="update_notification" msgid="8677916482672588779">"buyekeza isaziso kusitayela sekholi eqhubekayo"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java
index 707c325..50556a1 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java
@@ -251,6 +251,7 @@
                     @Override
                     public void onResult(CallControl callControl) {
                         Log.i(TAG, "addCall: onResult: callback fired");
+                        Utils.postIncomingCallStyleNotification(getApplicationContext());
                         mVoipCall.onAddCallControl(callControl);
                         updateCallId();
                         updateCurrentEndpoint();
@@ -275,7 +276,8 @@
         mAudioRecord.stop();
         try {
             mAudioRecord.unregisterAudioRecordingCallback(mAudioRecordingCallback);
-        } catch (IllegalArgumentException e) {
+            Utils.clearNotification(getApplicationContext());
+        } catch (Exception e) {
             // pass through
         }
     }
diff --git a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/Utils.java b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/Utils.java
index 0de2b19..ec448b2 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/Utils.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/Utils.java
@@ -77,11 +77,16 @@
                         pendingAnswer, pendingReject)
                 )
                 .setFullScreenIntent(pendingAnswer, true)
+                .setOngoing(true)
                 .build();
 
         return callStyleNotification;
     }
 
+    public static void postIncomingCallStyleNotification(Context context) {
+        NotificationManager nm = context.getSystemService(NotificationManager.class);
+        nm.notify(Utils.CALL_NOTIFICATION_ID, createCallStyleNotification(context));
+    }
 
     public static void updateCallStyleNotification_toOngoingCall(Context context) {
         PendingIntent ongoingCall = PendingIntent.getActivity(context, 0,
@@ -97,6 +102,7 @@
                         ongoingCall)
                 )
                 .setFullScreenIntent(ongoingCall, true)
+                .setOngoing(true)
                 .build();
 
         NotificationManager notificationManager =
@@ -105,6 +111,14 @@
         notificationManager.notify(CALL_NOTIFICATION_ID, callStyleNotification);
     }
 
+    public static void clearNotification(Context context) {
+        NotificationManager notificationManager =
+                context.getSystemService(NotificationManager.class);
+        if (notificationManager != null) {
+            notificationManager.cancel(CALL_NOTIFICATION_ID);
+        }
+    }
+
     public static MediaPlayer createMediaPlayer(Context context) {
         int audioToPlay = (Math.random() > 0.5f) ?
                 com.android.server.telecom.transactionalVoipApp.R.raw.sample_audio :
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 7578b9d..72a3906 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/VoipAppMainActivity.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/VoipAppMainActivity.java
@@ -99,8 +99,6 @@
     }
 
     private void startInCallActivity(int direction) {
-        mNotificationManager.notify(Utils.CALL_NOTIFICATION_ID,
-                Utils.createCallStyleNotification(getApplicationContext()));
         Bundle extras = new Bundle();
         extras.putInt(Utils.sCALL_DIRECTION_KEY, direction);
         Intent intent = new Intent(getApplicationContext(), InCallActivity.class);
@@ -142,6 +140,7 @@
     protected void onDestroy() {
         Log.i(TAG, ACT_STATE_TAG + " onDestroy: is called before the activity is"
                 + " destroyed. ");
+        Utils.clearNotification(getApplicationContext());
         super.onDestroy();
     }
 }
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
index de57984..2dc077a 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
@@ -423,6 +423,7 @@
         List<AudioDeviceInfo> devices = new ArrayList<>();
         devices.add(mockAudioDeviceInfo);
 
+        when(mockAudioManager.getCommunicationDevice()).thenReturn(mSpeakerInfo);
         when(mockAudioManager.getAvailableCommunicationDevices())
                 .thenReturn(devices);
         when(mockAudioManager.setCommunicationDevice(eq(mockAudioDeviceInfo)))
@@ -458,6 +459,7 @@
         List<AudioDeviceInfo> devices = new ArrayList<>();
         devices.add(mockAudioDeviceInfo);
 
+        when(mockAudioManager.getCommunicationDevice()).thenReturn(mSpeakerInfo);
         when(mockAudioManager.getAvailableCommunicationDevices())
                         .thenReturn(devices);
         when(mockAudioManager.setCommunicationDevice(mockAudioDeviceInfo))
@@ -733,6 +735,24 @@
 
     @SmallTest
     @Test
+    public void testConnectedDevicesDoNotContainDuplicateDevices() {
+        BluetoothDevice hfpDevice = mock(BluetoothDevice.class);
+        when(hfpDevice.getAddress()).thenReturn("00:00:00:00:00:00");
+        when(hfpDevice.getType()).thenReturn(BluetoothDeviceManager.DEVICE_TYPE_HEADSET);
+        BluetoothDevice leDevice = mock(BluetoothDevice.class);
+        when(hfpDevice.getAddress()).thenReturn("00:00:00:00:00:00");
+        when(hfpDevice.getType()).thenReturn(BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO);
+
+        mBluetoothDeviceManager.onDeviceConnected(hfpDevice,
+                BluetoothDeviceManager.DEVICE_TYPE_HEADSET);
+        mBluetoothDeviceManager.onDeviceConnected(leDevice,
+                BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO);
+
+        assertEquals(1, mBluetoothDeviceManager.getNumConnectedDevices());
+    }
+
+    @SmallTest
+    @Test
     public void testInBandRingingEnabledForLeDevice() {
         when(mBluetoothHeadset.isInbandRingingEnabled()).thenReturn(false);
         when(mBluetoothLeAudio.isInbandRingtoneEnabled(1)).thenReturn(true);
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
index b31ad5f..15a81d4 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
@@ -67,7 +67,7 @@
 public class BluetoothRouteTransitionTests extends TelecomTestCase {
     private enum ListenerUpdate {
         DEVICE_LIST_CHANGED, ACTIVE_DEVICE_PRESENT, ACTIVE_DEVICE_GONE,
-        AUDIO_CONNECTED, AUDIO_DISCONNECTED, UNEXPECTED_STATE_CHANGE
+        AUDIO_CONNECTING, AUDIO_CONNECTED, AUDIO_DISCONNECTED, UNEXPECTED_STATE_CHANGE
     }
 
     private static class BluetoothRouteTestParametersBuilder {
@@ -350,6 +350,9 @@
                 case ACTIVE_DEVICE_GONE:
                     verify(mListener).onBluetoothActiveDeviceGone();
                     break;
+                case AUDIO_CONNECTING:
+                    verify(mListener).onBluetoothAudioConnecting();
+                    break;
                 case AUDIO_CONNECTED:
                     verify(mListener).onBluetoothAudioConnected();
                     break;
@@ -452,7 +455,7 @@
                 .setConnectedDevices(DEVICE2, DEVICE1)
                 .setActiveDevice(DEVICE1)
                 .setMessageType(BluetoothRouteManager.CONNECT_BT)
-                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTING)
                 .setExpectedBluetoothInteraction(CONNECT)
                 .setExpectedConnectionDevice(DEVICE1)
                 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
@@ -508,7 +511,7 @@
                 .setConnectedDevices(DEVICE2, DEVICE1, DEVICE3)
                 .setMessageType(BluetoothRouteManager.CONNECT_BT)
                 .setMessageDevice(DEVICE3)
-                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTING)
                 .setExpectedBluetoothInteraction(CONNECT_SWITCH_DEVICE)
                 .setExpectedConnectionDevice(DEVICE3)
                 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
@@ -522,7 +525,7 @@
                 .setConnectedDevices(DEVICE2, DEVICE1, DEVICE3)
                 .setMessageType(BluetoothRouteManager.CONNECT_BT)
                 .setMessageDevice(DEVICE3)
-                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTING)
                 .setExpectedBluetoothInteraction(CONNECT_SWITCH_DEVICE)
                 .setExpectedConnectionDevice(DEVICE3)
                 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java b/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
index 3d06ad0..c8ceea9 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
@@ -59,6 +59,7 @@
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -277,7 +278,8 @@
         verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
         assertMessageArgEquality(expectedArgs, captor.getValue());
-        verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs(
+        // Expet another invocation due to audio mode change signal.
+        verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs(
                 anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
 
 
@@ -286,7 +288,7 @@
         verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
         assertMessageArgEquality(expectedArgs, captor.getValue());
-        verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs(
+        verify(mCallAudioModeStateMachine, times(4)).sendMessageWithArgs(
                 anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
 
         disconnectCall(call);
@@ -327,7 +329,8 @@
         verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
         assertMessageArgEquality(expectedArgs, captor.getValue());
-        verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs(
+        // Expect an extra time due to audio mode change signal
+        verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs(
                 anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
 
         // Ensure we started ringback.
@@ -702,6 +705,73 @@
         assertFalse(captor.getValue().isStreaming);
     }
 
+    @SmallTest
+    @Test
+    public void testTriggerAudioManagerModeChange() {
+        // Start with an incoming PSTN call
+        Call pstnCall = mock(Call.class);
+        when(pstnCall.getState()).thenReturn(CallState.RINGING);
+        when(pstnCall.getIsVoipAudioMode()).thenReturn(false);
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
+
+        // Add the call
+        mCallAudioManager.onCallAdded(pstnCall);
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE), captor.capture());
+        CallAudioModeStateMachine.MessageArgs expectedArgs =
+                new Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(true)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setHasAudioProcessingCalls(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .setForegroundCallIsVoip(false)
+                        .build();
+        assertMessageArgEquality(expectedArgs, captor.getValue());
+        clearInvocations(mCallAudioModeStateMachine); // Avoid verifying for previous calls
+
+        // Make call active; don't expect there to be an audio mode transition.
+        when(pstnCall.getState()).thenReturn(CallState.ACTIVE);
+        mCallAudioManager.onCallStateChanged(pstnCall, CallState.RINGING, CallState.ACTIVE);
+        verify(mCallAudioModeStateMachine, never()).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE),
+                any(CallAudioModeStateMachine.MessageArgs.class));
+        clearInvocations(mCallAudioModeStateMachine); // Avoid verifying for previous calls
+
+        // Add a new Voip call in ringing state; this should not result in a direct audio mode
+        // change.
+        Call voipCall = mock(Call.class);
+        when(voipCall.getState()).thenReturn(CallState.RINGING);
+        when(voipCall.getIsVoipAudioMode()).thenReturn(true);
+        mCallAudioManager.onCallAdded(voipCall);
+        verify(mCallAudioModeStateMachine, never()).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE),
+                any(CallAudioModeStateMachine.MessageArgs.class));
+        clearInvocations(mCallAudioModeStateMachine); // Avoid verifying for previous calls
+
+        // Make voip call active and set the PSTN call to locally disconnecting; the new foreground
+        // call will be the voip call.
+        when(pstnCall.isLocallyDisconnecting()).thenReturn(true);
+        when(voipCall.getState()).thenReturn(CallState.ACTIVE);
+        mCallAudioManager.onCallStateChanged(voipCall, CallState.RINGING, CallState.ACTIVE);
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE), captor.capture());
+        CallAudioModeStateMachine.MessageArgs expectedArgs2 =
+                new Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setHasAudioProcessingCalls(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .setForegroundCallIsVoip(true)
+                        .build();
+        assertMessageArgEquality(expectedArgs2, captor.getValue());
+    }
+
     private Call createSimulatedRingingCall() {
         Call call = mock(Call.class);
         when(call.getState()).thenReturn(CallState.SIMULATED_RINGING);
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRoutePeripheralAdapterTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRoutePeripheralAdapterTest.java
index dfe1483..2fc6ec6 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRoutePeripheralAdapterTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRoutePeripheralAdapterTest.java
@@ -26,6 +26,7 @@
 
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.server.telecom.AsyncRingtonePlayer;
 import com.android.server.telecom.CallAudioRoutePeripheralAdapter;
 import com.android.server.telecom.CallAudioRouteStateMachine;
 import com.android.server.telecom.DockManager;
@@ -47,6 +48,7 @@
     @Mock private BluetoothRouteManager mBluetoothRouteManager;
     @Mock private WiredHeadsetManager mWiredHeadsetManager;
     @Mock private DockManager mDockManager;
+    @Mock private AsyncRingtonePlayer mRingtonePlayer;
 
     @Override
     @Before
@@ -57,7 +59,8 @@
                 mCallAudioRouteStateMachine,
                 mBluetoothRouteManager,
                 mWiredHeadsetManager,
-                mDockManager);
+                mDockManager,
+                mRingtonePlayer);
     }
 
     @Override
@@ -126,6 +129,16 @@
         mAdapter.onBluetoothAudioConnected();
         verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo(
                 CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
+        verify(mRingtonePlayer).updateBtActiveState(true);
+    }
+
+    @SmallTest
+    @Test
+    public void testOnBluetoothAudioConnecting() {
+        mAdapter.onBluetoothAudioConnecting();
+        verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
+        verify(mRingtonePlayer).updateBtActiveState(false);
     }
 
     @SmallTest
@@ -134,6 +147,7 @@
         mAdapter.onBluetoothAudioDisconnected();
         verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo(
                 CallAudioRouteStateMachine.BT_AUDIO_DISCONNECTED);
+        verify(mRingtonePlayer).updateBtActiveState(false);
     }
 
     @SmallTest
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
index 5b3d6f1..d93cb2c 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
@@ -60,6 +60,7 @@
 import java.util.Set;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
@@ -694,6 +695,16 @@
         verify(mockBluetoothRouteManager, atLeastOnce())
                 .connectBluetoothAudio(eq(bluetoothDevice1.getAddress()));
         assertTrue(stateMachine.isInActiveState());
+
+        // Switch to inactive, pretending that the call disconnected.
+        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
+                CallAudioRouteStateMachine.NO_FOCUS);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+
+        // Make sure that we've successfully switched to the quiescent BT route
+        assertEquals(CallAudioState.ROUTE_BLUETOOTH,
+                stateMachine.getCurrentCallAudioState().getRoute());
+        assertFalse(stateMachine.isInActiveState());
     }
 
     @SmallTest
diff --git a/tests/src/com/android/server/telecom/tests/CallEndpointControllerTest.java b/tests/src/com/android/server/telecom/tests/CallEndpointControllerTest.java
index f4008aa..b81479b 100644
--- a/tests/src/com/android/server/telecom/tests/CallEndpointControllerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallEndpointControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.server.telecom.tests;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -50,7 +51,9 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 @RunWith(JUnit4.class)
 public class CallEndpointControllerTest extends TelecomTestCase {
@@ -81,6 +84,9 @@
             availableBluetooth1);
     private static final CallAudioState audioState7 = new CallAudioState(false,
             CallAudioState.ROUTE_STREAMING, CallAudioState.ROUTE_ALL, null, availableBluetooth1);
+    private static final CallAudioState audioState8 = new CallAudioState(false,
+            CallAudioState.ROUTE_EARPIECE, CallAudioState.ROUTE_ALL, bluetoothDevice1,
+            availableBluetooth2);
 
     private CallEndpointController mCallEndpointController;
 
@@ -177,6 +183,65 @@
         verify(mConnectionService, never()).onMuteStateChanged(any(), anyBoolean());
     }
 
+    /**
+     * Ensure that {@link CallAudioManager#setAudioRoute(int, String)} is invoked when the user
+     * requests to switch to a bluetooth CallEndpoint.  This is an edge case where bluetooth is not
+     * the current CallEndpoint but the CallAudioState shows the bluetooth device is
+     * active/available.
+     */
+    @Test
+    public void testSwitchFromEarpieceToBluetooth() {
+        // simulate an audio state where the EARPIECE is active but a bluetooth device is active.
+        mCallEndpointController.onCallAudioStateChanged(null, audioState8 /* Ear but BT active */);
+        CallEndpoint btEndpoint = mCallEndpointController.getAvailableEndpoints().stream()
+                .filter(e -> e.getEndpointType() == CallEndpoint.TYPE_BLUETOOTH)
+                .toList().get(0); // get the only available BT endpoint
+
+        // verify the CallEndpointController shows EARPIECE active + BT endpoint is active device
+        assertEquals(CallEndpoint.TYPE_EARPIECE,
+                mCallEndpointController.getCurrentCallEndpoint().getEndpointType());
+        assertNotNull(btEndpoint);
+
+        // request an endpoint change from earpiece to the bluetooth
+        doReturn(audioState8).when(mCallAudioManager).getCallAudioState();
+        mCallEndpointController.requestCallEndpointChange(btEndpoint, mResultReceiver);
+
+        // verify the transaction was successful and CallAudioManager#setAudioRoute was called
+        verify(mResultReceiver, never()).send(eq(CallEndpoint.ENDPOINT_OPERATION_FAILED), any());
+        verify(mCallAudioManager, times(1)).setAudioRoute(eq(CallAudioState.ROUTE_BLUETOOTH),
+                eq(bluetoothDevice1.getAddress()));
+    }
+
+
+    /**
+     * Ensure that {@link CallAudioManager#setAudioRoute(int, String)} is invoked when the user
+     * requests to switch to from one bluetooth device to another.
+     */
+    @Test
+    public void testBtDeviceSwitch() {
+        // bluetoothDevice1 should start as active and bluetoothDevice2 is available
+        mCallEndpointController.onCallAudioStateChanged(null, audioState2 /* BT active D1 */);
+        CallEndpoint currentEndpoint = mCallEndpointController.getCurrentCallEndpoint();
+        List<CallEndpoint> btEndpoints = mCallEndpointController.getAvailableEndpoints().stream()
+                .filter(e -> e.getEndpointType() == CallEndpoint.TYPE_BLUETOOTH)
+                .toList(); // get the only available BT endpoint
+
+        // verify the initial state of the test
+        assertEquals(2, btEndpoints.size());
+        assertEquals(CallEndpoint.TYPE_BLUETOOTH, currentEndpoint.getEndpointType());
+        assertEquals(currentEndpoint, btEndpoints.get(0));
+        assertNotEquals(currentEndpoint, btEndpoints.get(1));
+
+        // request an endpoint change from BT D1 --> BT D2
+        doReturn(audioState2).when(mCallAudioManager).getCallAudioState();
+        mCallEndpointController.requestCallEndpointChange(btEndpoints.get(1), mResultReceiver);
+
+        // verify the transaction was successful and CallAudioManager#setAudioRoute was called
+        verify(mResultReceiver, never()).send(eq(CallEndpoint.ENDPOINT_OPERATION_FAILED), any());
+        verify(mCallAudioManager, times(1)).setAudioRoute(eq(CallAudioState.ROUTE_BLUETOOTH),
+                eq(bluetoothDevice2.getAddress()));
+    }
+
     @Test
     public void testAvailableEndpointChanged() throws Exception {
         mCallEndpointController.onCallAudioStateChanged(audioState1, audioState6);
diff --git a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
index b9f5667..9466220 100644
--- a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
@@ -1062,7 +1062,7 @@
         when(fakeCall.getVideoStateHistory()).thenReturn(callVideoState);
         when(fakeCall.getPostDialDigits()).thenReturn(postDialDigits);
         when(fakeCall.getViaNumber()).thenReturn(viaNumber);
-        when(fakeCall.getInitiatingUser()).thenReturn(initiatingUser);
+        when(fakeCall.getAssociatedUser()).thenReturn(initiatingUser);
         when(fakeCall.getCallDataUsage()).thenReturn(callDataUsage);
         when(fakeCall.isEmergencyCall()).thenReturn(
                 phoneAccountHandle.equals(EMERGENCY_ACCT_HANDLE));
diff --git a/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java b/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
index d95a0e2..4d8d497 100644
--- a/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
@@ -129,7 +129,7 @@
 
         when(mCallsManager.getCurrentUserHandle()).thenReturn(UserHandle.CURRENT);
         when(mCall.getId()).thenReturn(CALL_ID);
-        when(mCall.getUserHandleFromTargetPhoneAccount()).
+        when(mCall.getAssociatedUser()).
                 thenReturn(PA_HANDLE.getUserHandle());
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mContext.getSystemService(TelecomManager.class))
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index ddb48ec..00be89f 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -62,7 +62,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.BlockedNumberContract;
-import android.provider.Telephony;
 import android.telecom.CallException;
 import android.telecom.CallScreeningService;
 import android.telecom.CallerInfo;
@@ -157,6 +156,8 @@
     private static final int TEST_TIMEOUT = 5000;  // milliseconds
     private static final long STATE_TIMEOUT = 5000L;
     private static final int SECONDARY_USER_ID = 12;
+    private static final UserHandle TEST_USER_HANDLE = UserHandle.of(123);
+    private static final String TEST_PACKAGE_NAME = "GoogleMeet";
     private static final PhoneAccountHandle SIM_1_HANDLE = new PhoneAccountHandle(
             ComponentName.unflattenFromString("com.foo/.Blah"), "Sim1");
     private static final PhoneAccountHandle SIM_1_HANDLE_SECONDARY = new PhoneAccountHandle(
@@ -174,6 +175,10 @@
             ComponentName.unflattenFromString("com.baz/.Self"), "Self");
     private static final PhoneAccountHandle SELF_MANAGED_2_HANDLE = new PhoneAccountHandle(
             ComponentName.unflattenFromString("com.baz/.Self2"), "Self2");
+    private static final PhoneAccountHandle WORK_HANDLE = new PhoneAccountHandle(
+            ComponentName.unflattenFromString("com.foo/.Blah"), "work", new UserHandle(10));
+    private static final PhoneAccountHandle SELF_MANAGED_W_CUSTOM_HANDLE = new PhoneAccountHandle(
+            new ComponentName(TEST_PACKAGE_NAME, "class"), "1", TEST_USER_HANDLE);
     private static final PhoneAccount SIM_1_ACCOUNT = new PhoneAccount.Builder(SIM_1_HANDLE, "Sim1")
             .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
                     | PhoneAccount.CAPABILITY_CALL_PROVIDER
@@ -203,6 +208,19 @@
             .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
             .setIsEnabled(true)
             .build();
+    private static final PhoneAccount WORK_ACCOUNT = new PhoneAccount.Builder(
+            WORK_HANDLE, "work")
+            .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
+                    | PhoneAccount.CAPABILITY_CALL_PROVIDER
+                    | PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
+            .setIsEnabled(true)
+            .build();
+    private static final PhoneAccount SM_W_DIFFERENT_PACKAGE_AND_USER = new PhoneAccount.Builder(
+            SELF_MANAGED_W_CUSTOM_HANDLE, "Self")
+            .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
+            .setIsEnabled(true)
+            .build();
+
     private static final Uri TEST_ADDRESS = Uri.parse("tel:555-1212");
     private static final Uri TEST_ADDRESS2 = Uri.parse("tel:555-1213");
     private static final Uri TEST_ADDRESS3 = Uri.parse("tel:555-1214");
@@ -329,6 +347,8 @@
                 mAccessibilityManagerAdapter,
                 // Just do async tasks synchronously to support testing.
                 command -> command.run(),
+                // For call audio tasks
+                command -> command.run(),
                 mBlockedNumbersAdapter,
                 TransactionManager.getTestInstance(),
                 mEmergencyCallDiagnosticLogger,
@@ -341,6 +361,8 @@
                 eq(SIM_1_HANDLE), any())).thenReturn(SIM_1_ACCOUNT);
         when(mPhoneAccountRegistrar.getPhoneAccount(
                 eq(SIM_2_HANDLE), any())).thenReturn(SIM_2_ACCOUNT);
+        when(mPhoneAccountRegistrar.getPhoneAccount(
+                eq(WORK_HANDLE), any())).thenReturn(WORK_ACCOUNT);
         when(mToastFactory.makeText(any(), anyInt(), anyInt())).thenReturn(mToast);
         when(mToastFactory.makeText(any(), any(), anyInt())).thenReturn(mToast);
     }
@@ -2476,7 +2498,7 @@
     public void testPostCallPackageNameSetOnSuccessfulOutgoingCall() throws Exception {
         Call outgoingCall = addSpyCall(CallState.NEW);
         when(mCallsManager.getRoleManagerAdapter().getDefaultCallScreeningApp(
-                outgoingCall.getUserHandleFromTargetPhoneAccount()))
+                outgoingCall.getAssociatedUser()))
                 .thenReturn(DEFAULT_CALL_SCREENING_APP);
         assertNull(outgoingCall.getPostCallPackageName());
         mCallsManager.onSuccessfulOutgoingCall(outgoingCall, CallState.CONNECTING);
@@ -2485,7 +2507,30 @@
 
     @SmallTest
     @Test
-    public void testRejectIncomingCallOnPAHInactive() throws Exception {
+    public void testRejectIncomingCallOnPAHInactive_SecondaryUser() throws Exception {
+        ConnectionServiceWrapper service = mock(ConnectionServiceWrapper.class);
+        doReturn(WORK_HANDLE.getComponentName()).when(service).getComponentName();
+        mCallsManager.addConnectionServiceRepositoryCache(WORK_HANDLE.getComponentName(),
+                WORK_HANDLE.getUserHandle(), service);
+
+        UserManager um = mContext.getSystemService(UserManager.class);
+        UserHandle newUser = new UserHandle(11);
+        when(mCallsManager.getCurrentUserHandle()).thenReturn(newUser);
+        when(um.isUserAdmin(eq(newUser.getIdentifier()))).thenReturn(false);
+        when(um.isQuietModeEnabled(eq(WORK_HANDLE.getUserHandle()))).thenReturn(false);
+        when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(eq(WORK_HANDLE)))
+                .thenReturn(WORK_ACCOUNT);
+        Call newCall = mCallsManager.processIncomingCallIntent(
+                WORK_HANDLE, new Bundle(), false);
+
+        verify(service, timeout(TEST_TIMEOUT)).createConnectionFailed(any());
+        assertFalse(newCall.isInECBM());
+        assertEquals(USER_MISSED_NOT_RUNNING, newCall.getMissedReason());
+    }
+
+    @SmallTest
+    @Test
+    public void testRejectIncomingCallOnPAHInactive_ProfilePaused() throws Exception {
         ConnectionServiceWrapper service = mock(ConnectionServiceWrapper.class);
         doReturn(SIM_2_HANDLE.getComponentName()).when(service).getComponentName();
         mCallsManager.addConnectionServiceRepositoryCache(SIM_2_HANDLE.getComponentName(),
@@ -2522,6 +2567,30 @@
 
     @SmallTest
     @Test
+    public void testAcceptIncomingCallOnPAHInactiveAndECBMActive_SecondaryUser() throws Exception {
+        ConnectionServiceWrapper service = mock(ConnectionServiceWrapper.class);
+        doReturn(WORK_HANDLE.getComponentName()).when(service).getComponentName();
+        mCallsManager.addConnectionServiceRepositoryCache(SIM_2_HANDLE.getComponentName(),
+                WORK_HANDLE.getUserHandle(), service);
+
+        when(mEmergencyCallHelper.isLastOutgoingEmergencyCallPAH(eq(WORK_HANDLE)))
+                .thenReturn(true);
+        UserManager um = mContext.getSystemService(UserManager.class);
+        UserHandle newUser = new UserHandle(11);
+        when(mCallsManager.getCurrentUserHandle()).thenReturn(newUser);
+        when(um.isUserAdmin(eq(newUser.getIdentifier()))).thenReturn(false);
+        when(um.isQuietModeEnabled(eq(WORK_HANDLE.getUserHandle()))).thenReturn(false);
+        when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(eq(WORK_HANDLE)))
+                .thenReturn(WORK_ACCOUNT);
+        Call newCall = mCallsManager.processIncomingCallIntent(
+                WORK_HANDLE, new Bundle(), false);
+
+        assertTrue(newCall.isInECBM());
+        verify(service, timeout(TEST_TIMEOUT).times(0)).createConnectionFailed(any());
+    }
+
+    @SmallTest
+    @Test
     public void testAcceptIncomingEmergencyCallOnPAHInactive() throws Exception {
         ConnectionServiceWrapper service = mock(ConnectionServiceWrapper.class);
         doReturn(SIM_2_HANDLE.getComponentName()).when(service).getComponentName();
@@ -3081,6 +3150,112 @@
                 eq(false));
     }
 
+    /**
+     * Verify CallsManager#isInSelfManagedCall(packageName, userHandle) returns true when
+     * CallsManager is first made aware of the incoming call in processIncomingCallIntent.
+     */
+    @SmallTest
+    @Test
+    public void testAddNewIncomingCall_IsInSelfManagedCall() {
+        // GIVEN
+        assertEquals(0, mCallsManager.getSelfManagedCallsBeingSetup().size());
+        assertFalse(mCallsManager.isInSelfManagedCall(TEST_PACKAGE_NAME, TEST_USER_HANDLE));
+
+        // WHEN
+        when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(any()))
+                .thenReturn(SM_W_DIFFERENT_PACKAGE_AND_USER);
+        UserManager um = mContext.getSystemService(UserManager.class);
+        when(um.isUserAdmin(eq(mCallsManager.getCurrentUserHandle().getIdentifier())))
+                .thenReturn(true);
+
+        // THEN
+        mCallsManager.processIncomingCallIntent(SELF_MANAGED_W_CUSTOM_HANDLE, new Bundle(), false);
+
+        assertEquals(1, mCallsManager.getSelfManagedCallsBeingSetup().size());
+        assertTrue(mCallsManager.isInSelfManagedCall(TEST_PACKAGE_NAME, TEST_USER_HANDLE));
+        assertEquals(0, mCallsManager.getCalls().size());
+    }
+
+    /**
+     * Verify CallsManager#isInSelfManagedCall(packageName, userHandle) returns true when
+     * CallsManager is first made aware of the outgoing call in StartOutgoingCall.
+     */
+    @SmallTest
+    @Test
+    public void testStartOutgoing_IsInSelfManagedCall() {
+        // GIVEN
+        assertEquals(0, mCallsManager.getSelfManagedCallsBeingSetup().size());
+        assertFalse(mCallsManager.isInSelfManagedCall(TEST_PACKAGE_NAME, TEST_USER_HANDLE));
+
+        // WHEN
+        when(mPhoneAccountRegistrar.getPhoneAccount(any(), any()))
+                .thenReturn(SM_W_DIFFERENT_PACKAGE_AND_USER);
+        // Ensure contact info lookup succeeds
+        doAnswer(invocation -> {
+            Uri handle = invocation.getArgument(0);
+            CallerInfo info = new CallerInfo();
+            CompletableFuture<Pair<Uri, CallerInfo>> callerInfoFuture = new CompletableFuture<>();
+            callerInfoFuture.complete(new Pair<>(handle, info));
+            return callerInfoFuture;
+        }).when(mCallerInfoLookupHelper).startLookup(any(Uri.class));
+        // Ensure we have candidate phone account handle info.
+        when(mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(any(), any())).thenReturn(
+                SELF_MANAGED_W_CUSTOM_HANDLE);
+        when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
+                any(), anyInt(), anyInt(), anyBoolean())).thenReturn(
+                new ArrayList<>(List.of(SELF_MANAGED_W_CUSTOM_HANDLE)));
+
+        // THEN
+        mCallsManager.startOutgoingCall(TEST_ADDRESS, SELF_MANAGED_W_CUSTOM_HANDLE, new Bundle(),
+                TEST_USER_HANDLE, new Intent(), TEST_PACKAGE_NAME);
+
+        assertEquals(1, mCallsManager.getSelfManagedCallsBeingSetup().size());
+        assertTrue(mCallsManager.isInSelfManagedCall(TEST_PACKAGE_NAME, TEST_USER_HANDLE));
+        assertEquals(0, mCallsManager.getCalls().size());
+    }
+
+    /**
+     * Verify SelfManagedCallsBeingSetup is being cleaned up in CallsManager#addCall and
+     * CallsManager#removeCall.  This ensures no memory leaks.
+     */
+    @SmallTest
+    @Test
+    public void testCallsBeingSetupCleanup() {
+        Call spyCall = addSpyCall();
+        assertEquals(0, mCallsManager.getSelfManagedCallsBeingSetup().size());
+        // verify CallsManager#removeCall removes the call from SelfManagedCallsBeingSetup
+        mCallsManager.addCallBeingSetup(spyCall);
+        mCallsManager.removeCall(spyCall);
+        assertEquals(0, mCallsManager.getSelfManagedCallsBeingSetup().size());
+        // verify CallsManager#addCall removes the call from SelfManagedCallsBeingSetup
+        mCallsManager.addCallBeingSetup(spyCall);
+        mCallsManager.addCall(spyCall);
+        assertEquals(0, mCallsManager.getSelfManagedCallsBeingSetup().size());
+    }
+
+    /**
+     * Verify isInSelfManagedCall returns false if there is a self-managed call, but it is for a
+     * different package and user
+     */
+    @SmallTest
+    @Test
+    public void testIsInSelfManagedCall_PackageUserQueryIsWorkingAsIntended() {
+        // start an active call
+        Call randomCall = createSpyCall(SELF_MANAGED_HANDLE, CallState.ACTIVE);
+        mCallsManager.addCallBeingSetup(randomCall);
+        assertEquals(1, mCallsManager.getSelfManagedCallsBeingSetup().size());
+        // query isInSelfManagedCall for a package that is NOT in a call;  expect false
+        assertFalse(mCallsManager.isInSelfManagedCall(TEST_PACKAGE_NAME, TEST_USER_HANDLE));
+        // start another call
+        Call targetCall = addSpyCall(SELF_MANAGED_W_CUSTOM_HANDLE, CallState.DIALING);
+        when(targetCall.getTargetPhoneAccount()).thenReturn(SELF_MANAGED_W_CUSTOM_HANDLE);
+        when(targetCall.isSelfManaged()).thenReturn(true);
+        mCallsManager.addCallBeingSetup(targetCall);
+        // query isInSelfManagedCall for a package that is in a call
+        assertTrue(mCallsManager.isInSelfManagedCall(TEST_PACKAGE_NAME, TEST_USER_HANDLE));
+    }
+
+
     private Call addSpyCall() {
         return addSpyCall(SIM_2_HANDLE, CallState.ACTIVE);
     }
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
index cba383d..0927b80 100755
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
@@ -70,6 +70,8 @@
     public CountDownLatch mExtrasLock = new CountDownLatch(1);
     static int NOT_SPECIFIED = 0;
     public static final String STATUS_HINTS_EXTRA = "updateStatusHints";
+    public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE =
+            new PhoneAccountHandle(new ComponentName("pkg", "cls"), "test");
 
     /**
      * Implementation of ConnectionService that performs no-ops for tasks normally meant for
@@ -196,7 +198,7 @@
 
     public class FakeConference extends Conference {
         public FakeConference() {
-            super(null);
+            super(TEST_PHONE_ACCOUNT_HANDLE);
             setConnectionCapabilities(
                     Connection.CAPABILITY_SUPPORT_HOLD
                             | Connection.CAPABILITY_HOLD
diff --git a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
index dbcab66..8a85a87 100644
--- a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
+++ b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
@@ -144,7 +144,7 @@
                 });
         when(mMockAccountRegistrar.getAllPhoneAccounts(any(UserHandle.class), anyBoolean()))
                 .thenReturn(phoneAccounts);
-        when(mMockCall.getUserHandleFromTargetPhoneAccount()).
+        when(mMockCall.getAssociatedUser()).
                 thenReturn(Binder.getCallingUserHandle());
     }
 
diff --git a/tests/src/com/android/server/telecom/tests/DisconnectedCallNotifierTest.java b/tests/src/com/android/server/telecom/tests/DisconnectedCallNotifierTest.java
index 2cdc23a..05c5071 100644
--- a/tests/src/com/android/server/telecom/tests/DisconnectedCallNotifierTest.java
+++ b/tests/src/com/android/server/telecom/tests/DisconnectedCallNotifierTest.java
@@ -151,6 +151,7 @@
         when(call.getDisconnectCause()).thenReturn(cause);
         when(call.getTargetPhoneAccount()).thenReturn(PHONE_ACCOUNT_HANDLE);
         when(call.getHandle()).thenReturn(TEL_CALL_HANDLE);
+        when(call.getAssociatedUser()).thenReturn(PHONE_ACCOUNT_HANDLE.getUserHandle());
         return call;
     }
 }
diff --git a/tests/src/com/android/server/telecom/tests/HeadsetMediaButtonTest.java b/tests/src/com/android/server/telecom/tests/HeadsetMediaButtonTest.java
index ce23724..0bfa987 100644
--- a/tests/src/com/android/server/telecom/tests/HeadsetMediaButtonTest.java
+++ b/tests/src/com/android/server/telecom/tests/HeadsetMediaButtonTest.java
@@ -18,7 +18,6 @@
 
 import android.content.Intent;
 import android.media.session.MediaSession;
-import android.telecom.CallEndpoint;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.view.KeyEvent;
@@ -81,7 +80,7 @@
     }
 
     /**
-     * Nominal case; just add a call and remove it; this happens when the audio state is earpiece.
+     * Nominal case; just add a call and remove it.
      */
     @SmallTest
     @Test
@@ -91,95 +90,14 @@
         when(mMockCallsManager.hasAnyCalls()).thenReturn(true);
         mHeadsetMediaButton.onCallAdded(regularCall);
         waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
-        verify(mMediaSessionAdapter, never()).setActive(eq(true));
-
-        // Report that the endpoint is earpiece and other routes that don't matter
-        mHeadsetMediaButton.onCallEndpointChanged(
-                new CallEndpoint("Earpiece", CallEndpoint.TYPE_EARPIECE));
-        mHeadsetMediaButton.onCallEndpointChanged(
-                new CallEndpoint("Speaker", CallEndpoint.TYPE_SPEAKER));
-        mHeadsetMediaButton.onCallEndpointChanged(
-                new CallEndpoint("BT", CallEndpoint.TYPE_BLUETOOTH));
-        waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
-        verify(mMediaSessionAdapter, never()).setActive(eq(true));
-
-        // ... and thus we see how the original code isn't amenable to tests.
-        when(mMediaSessionAdapter.isActive()).thenReturn(false);
-
-        // Still should not have done anything; we never hit wired headset
-        mHeadsetMediaButton.onCallRemoved(regularCall);
-        waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
-        verify(mMediaSessionAdapter, never()).setActive(eq(false));
-    }
-
-    /**
-     * Call is added and then routed to headset after call start
-     */
-    @SmallTest
-    @Test
-    public void testAddCallThenRouteToHeadset() {
-        Call regularCall = getRegularCall();
-
-        mHeadsetMediaButton.onCallAdded(regularCall);
-        waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
-        verify(mMediaSessionAdapter, never()).setActive(eq(true));
-
-        mHeadsetMediaButton.onCallEndpointChanged(
-                new CallEndpoint("Wired Headset", CallEndpoint.TYPE_WIRED_HEADSET));
-        waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
         verify(mMediaSessionAdapter).setActive(eq(true));
-
         // ... and thus we see how the original code isn't amenable to tests.
         when(mMediaSessionAdapter.isActive()).thenReturn(true);
 
-        // Remove the one call; we should release the session.
+        when(mMockCallsManager.hasAnyCalls()).thenReturn(false);
         mHeadsetMediaButton.onCallRemoved(regularCall);
         waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
         verify(mMediaSessionAdapter).setActive(eq(false));
-        when(mMediaSessionAdapter.isActive()).thenReturn(false);
-
-        // Add a new call; make sure we go active once more.
-        mHeadsetMediaButton.onCallAdded(regularCall);
-        mHeadsetMediaButton.onCallEndpointChanged(
-                new CallEndpoint("Wired Headset", CallEndpoint.TYPE_WIRED_HEADSET));
-        waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
-        verify(mMediaSessionAdapter, times(2)).setActive(eq(true));
-    }
-
-    /**
-     * Call is added and then routed to headset after call start
-     */
-    @SmallTest
-    @Test
-    public void testAddCallThenRouteToHeadsetAndBack() {
-        Call regularCall = getRegularCall();
-
-        when(mMockCallsManager.hasAnyCalls()).thenReturn(true);
-        mHeadsetMediaButton.onCallAdded(regularCall);
-        waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
-        verify(mMediaSessionAdapter, never()).setActive(eq(true));
-
-        mHeadsetMediaButton.onCallEndpointChanged(
-                new CallEndpoint("Wired Headset", CallEndpoint.TYPE_WIRED_HEADSET));
-        waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
-        verify(mMediaSessionAdapter).setActive(eq(true));
-
-        // ... and thus we see how the original code isn't amenable to tests.
-        when(mMediaSessionAdapter.isActive()).thenReturn(true);
-
-        mHeadsetMediaButton.onCallEndpointChanged(
-                new CallEndpoint("Earpiece", CallEndpoint.TYPE_EARPIECE));
-        waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
-        verify(mMediaSessionAdapter).setActive(eq(false));
-        when(mMediaSessionAdapter.isActive()).thenReturn(false);
-
-        // Remove the one call; we should not release again.
-        mHeadsetMediaButton.onCallRemoved(regularCall);
-        waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
-        // Remember, mockito counts total invocations; we should have went active once and then
-        // inactive again when we hit earpiece.
-        verify(mMediaSessionAdapter, times(1)).setActive(eq(true));
-        verify(mMediaSessionAdapter, times(1)).setActive(eq(false));
     }
 
     /**
@@ -193,8 +111,6 @@
         // Start with a regular old call.
         when(mMockCallsManager.hasAnyCalls()).thenReturn(true);
         mHeadsetMediaButton.onCallAdded(regularCall);
-        mHeadsetMediaButton.onCallEndpointChanged(
-                new CallEndpoint("Wired Headset", CallEndpoint.TYPE_WIRED_HEADSET));
         waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
         verify(mMediaSessionAdapter).setActive(eq(true));
         when(mMediaSessionAdapter.isActive()).thenReturn(true);
@@ -206,7 +122,6 @@
         // Expect to set session inactive.
         waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
         verify(mMediaSessionAdapter).setActive(eq(false));
-        when(mMediaSessionAdapter.isActive()).thenReturn(false);
 
         // For good measure lets make it non-external again.
         when(regularCall.isExternalCall()).thenReturn(false);
@@ -214,7 +129,7 @@
         mHeadsetMediaButton.onExternalCallChanged(regularCall, false);
         // Expect to set session active.
         waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
-        verify(mMediaSessionAdapter, times(2)).setActive(eq(true));
+        verify(mMediaSessionAdapter).setActive(eq(true));
     }
 
     @MediumTest
@@ -224,8 +139,6 @@
         when(externalCall.isExternalCall()).thenReturn(true);
 
         mHeadsetMediaButton.onCallAdded(externalCall);
-        mHeadsetMediaButton.onCallEndpointChanged(
-                new CallEndpoint("Wired Headset", CallEndpoint.TYPE_WIRED_HEADSET));
         waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
         verify(mMediaSessionAdapter, never()).setActive(eq(true));
 
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index 4c8d8e2..683a5e2 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -22,11 +22,13 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.matches;
 import static org.mockito.ArgumentMatchers.nullable;
@@ -34,6 +36,7 @@
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -154,6 +157,7 @@
     @Mock private AnomalyReporterAdapter mAnomalyReporterAdapter;
     @Mock UserManager mMockUserManager;
     @Mock UserInfo mMockUserInfo;
+    @Mock UserInfo mMockChildUserInfo; //work profile
 
     @Rule
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
@@ -198,13 +202,17 @@
         | Context.BIND_FOREGROUND_SERVICE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
         | Context.BIND_SCHEDULE_LIKE_TOP_APP;
 
+    private UserHandle mChildUserHandle = UserHandle.of(10);
+    private @Mock Call mMockChildUserCall;
+    private UserHandle mParentUserHandle = UserHandle.of(1);
+
     @Override
     @Before
     public void setUp() throws Exception {
         super.setUp();
         MockitoAnnotations.initMocks(this);
         when(mMockCall.getAnalytics()).thenReturn(new Analytics.CallInfo());
-        when(mMockCall.getUserHandleFromTargetPhoneAccount()).thenReturn(mUserHandle);
+        when(mMockCall.getAssociatedUser()).thenReturn(mUserHandle);
         when(mMockCall.getId()).thenReturn("TC@1");
         doReturn(mMockResources).when(mMockContext).getResources();
         doReturn(mMockAppOpsManager).when(mMockContext).getSystemService(AppOpsManager.class);
@@ -296,6 +304,8 @@
         when(mMockCallsManager.getAudioState()).thenReturn(new CallAudioState(false, 0, 0));
 
         when(mMockContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mMockUserManager);
+        when(mMockContext.getSystemService(eq(UserManager.class)))
+                .thenReturn(mMockUserManager);
         // Mock user info to allow binding on user stored in the phone account (mUserHandle).
         when(mMockUserManager.getUserInfo(anyInt())).thenReturn(mMockUserInfo);
         when(mMockUserInfo.isManagedProfile()).thenReturn(true);
@@ -414,6 +424,7 @@
         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
         when(mMockCallsManager.isInEmergencyCall()).thenReturn(false);
         when(mMockCall.isIncoming()).thenReturn(false);
+        when(mMockCall.getAssociatedUser()).thenReturn(mUserHandle);
         when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
         when(mMockCall.getIntentExtras()).thenReturn(callExtras);
         when(mMockCall.isExternalCall()).thenReturn(false);
@@ -451,6 +462,7 @@
         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
         when(mMockCallsManager.isInEmergencyCall()).thenReturn(false);
         when(mMockCall.isIncoming()).thenReturn(false);
+        when(mMockCall.getAssociatedUser()).thenReturn(mUserHandle);
         when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
         when(mMockCall.getIntentExtras()).thenReturn(callExtras);
         when(mMockCall.isExternalCall()).thenReturn(false);
@@ -507,6 +519,7 @@
             .thenReturn(mMockUserManager);
         when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
         when(mMockCall.isIncoming()).thenReturn(false);
+        when(mMockCall.getAssociatedUser()).thenReturn(mUserHandle);
         when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
         when(mMockCall.getIntentExtras()).thenReturn(callExtras);
         when(mMockCall.isExternalCall()).thenReturn(false);
@@ -573,7 +586,8 @@
         when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
         when(mMockCall.isEmergencyCall()).thenReturn(true);
-        when(mMockCall.getUserHandleFromTargetPhoneAccount()).thenReturn(DUMMY_USER_HANDLE);
+        when(mMockCall.isIncoming()).thenReturn(true);
+        when(mMockCall.getAssociatedUser()).thenReturn(DUMMY_USER_HANDLE);
         when(mMockContext.getSystemService(eq(UserManager.class)))
             .thenReturn(mMockUserManager);
         when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(true);
@@ -601,7 +615,8 @@
         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
         when(mMockCall.isEmergencyCall()).thenReturn(false);
         when(mMockCall.isInECBM()).thenReturn(true);
-        when(mMockCall.getUserHandleFromTargetPhoneAccount()).thenReturn(DUMMY_USER_HANDLE);
+        when(mMockCall.isIncoming()).thenReturn(true);
+        when(mMockCall.getAssociatedUser()).thenReturn(DUMMY_USER_HANDLE);
         when(mMockContext.getSystemService(eq(UserManager.class)))
                 .thenReturn(mMockUserManager);
         when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(true);
@@ -623,15 +638,47 @@
     @MediumTest
     @Test
     public void
+    testBindToService_UserAssociatedWithCallSecondary_NonEmergCallECBM_BindsToSecondaryUser()
+            throws Exception {
+        UserHandle newUser = new UserHandle(13);
+        when(mMockCallsManager.getCurrentUserHandle()).thenReturn(newUser);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        when(mMockCall.isEmergencyCall()).thenReturn(false);
+        when(mMockCall.isInECBM()).thenReturn(true);
+        when(mMockCall.isIncoming()).thenReturn(true);
+        when(mMockCall.getAssociatedUser()).thenReturn(DUMMY_USER_HANDLE);
+        when(mMockContext.getSystemService(eq(UserManager.class)))
+                .thenReturn(mMockUserManager);
+        when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
+        when(mMockUserManager.isUserAdmin(anyInt())).thenReturn(false);
+        setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
+        setupMockPackageManagerLocationPermission(SYS_PKG, false /* granted */);
+
+        mInCallController.bindToServices(mMockCall);
+
+        ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mMockContext, times(1)).bindServiceAsUser(
+                bindIntentCaptor.capture(),
+                any(ServiceConnection.class),
+                eq(serviceBindingFlags),
+                eq(newUser));
+        Intent bindIntent = bindIntentCaptor.getValue();
+        assertEquals(InCallService.SERVICE_INTERFACE, bindIntent.getAction());
+    }
+
+    @MediumTest
+    @Test
+    public void
     testBindToService_UserAssociatedWithCallNotInQuietMode_EmergCallInCallUi_BindsToAssociatedUser()
         throws Exception {
         when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
         when(mMockCall.isEmergencyCall()).thenReturn(true);
-        when(mMockCall.getUserHandleFromTargetPhoneAccount()).thenReturn(DUMMY_USER_HANDLE);
+        when(mMockCall.getAssociatedUser()).thenReturn(DUMMY_USER_HANDLE);
         when(mMockContext.getSystemService(eq(UserManager.class)))
             .thenReturn(mMockUserManager);
         when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
+        when(mMockUserManager.isUserAdmin(anyInt())).thenReturn(true);
         setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
         setupMockPackageManagerLocationPermission(SYS_PKG, false /* granted */);
 
@@ -667,6 +714,7 @@
             .thenReturn(mMockUserManager);
         when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
         when(mMockCall.isIncoming()).thenReturn(false);
+        when(mMockCall.getAssociatedUser()).thenReturn(mUserHandle);
         when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
         when(mMockCall.getIntentExtras()).thenReturn(callExtras);
         when(mMockCall.isExternalCall()).thenReturn(false);
@@ -753,6 +801,7 @@
         when(mMockCallsManager.getAudioState()).thenReturn(null);
         when(mMockCallsManager.canAddCall()).thenReturn(false);
         when(mMockCall.isIncoming()).thenReturn(false);
+        when(mMockCall.getAssociatedUser()).thenReturn(mUserHandle);
         when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
         when(mMockCall.getIntentExtras()).thenReturn(callExtras);
         when(mMockCall.isExternalCall()).thenReturn(false);
@@ -833,6 +882,7 @@
         when(mMockCallsManager.isInEmergencyCall()).thenReturn(false);
         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
         when(mMockCall.isIncoming()).thenReturn(false);
+        when(mMockCall.getAssociatedUser()).thenReturn(mUserHandle);
         when(mMockCall.isExternalCall()).thenReturn(false);
         when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
         when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
@@ -945,6 +995,7 @@
         when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
         when(mMockCall.getIntentExtras()).thenReturn(callExtras);
         when(mMockCall.isExternalCall()).thenReturn(false);
+        when(mMockCall.getAssociatedUser()).thenReturn(mUserHandle);
         when(mDefaultDialerCache.getDefaultDialerApplication(CURRENT_USER_ID))
                 .thenReturn(DEF_PKG);
         ArgumentCaptor<ServiceConnection> serviceConnectionCaptor =
@@ -1051,6 +1102,7 @@
                 .thenReturn(mMockUserManager);
         when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
         when(mMockCall.isIncoming()).thenReturn(false);
+        when(mMockCall.getAssociatedUser()).thenReturn(mUserHandle);
         when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
         when(mMockCall.getIntentExtras()).thenReturn(callExtras);
         when(mMockCall.isExternalCall()).thenReturn(false);
@@ -1502,6 +1554,7 @@
         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
         when(mMockCallsManager.isInEmergencyCall()).thenReturn(false);
         when(mMockCall.isIncoming()).thenReturn(false);
+        when(mMockCall.getAssociatedUser()).thenReturn(mUserHandle);
         when(mMockCall.isExternalCall()).thenReturn(false);
         when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
         when(mDefaultDialerCache.getDefaultDialerApplication(CURRENT_USER_ID)).thenReturn(DEF_PKG);
@@ -1736,6 +1789,118 @@
                 eq(UserHandle.CURRENT));
     }
 
+    @Test
+    public void testGetUserFromCall_TargetPhoneAccountNotSet() throws Exception {
+        setupMocks(false /* isExternalCall */);
+        setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
+        UserHandle testUser = new UserHandle(10);
+
+        when(mMockCall.getTargetPhoneAccount()).thenReturn(null);
+        when(mMockCall.getAssociatedUser()).thenReturn(testUser);
+
+        // Bind to ICS. The mapping should've been inserted with the testUser as the key.
+        mInCallController.bindToServices(mMockCall);
+        assertTrue(mInCallController.getInCallServiceConnections().containsKey(testUser));
+
+        // Set the target phone account. Simulates the flow when the user has chosen which sim to
+        // place the call on.
+        when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
+
+        // Remove the call. This invokes getUserFromCall to remove the ICS mapping.
+        when(mMockCallsManager.getCalls()).thenReturn(Collections.emptyList());
+        mInCallController.onCallRemoved(mMockCall);
+        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+
+        // Verify that the mapping was properly removed.
+        assertNull(mInCallController.getInCallServiceConnections().get(testUser));
+    }
+
+    @Test
+    public void testGetUserFromCall_IncomingCall() throws Exception {
+        setupMocks(false /* isExternalCall */);
+        setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
+        // Explicitly test on a different user to avoid interference with current user.
+        UserHandle testUser = new UserHandle(10);
+
+        // Set user handle in target phone account to test user
+        when(mMockCall.getAssociatedUser()).thenReturn(testUser);
+        when(mMockCall.isIncoming()).thenReturn(true);
+
+        // Bind to ICS. The mapping should've been inserted with the testUser as the key.
+        mInCallController.bindToServices(mMockCall);
+        assertTrue(mInCallController.getInCallServiceConnections().containsKey(testUser));
+
+        // Remove the call. This invokes getUserFromCall to remove the ICS mapping.
+        when(mMockCallsManager.getCalls()).thenReturn(Collections.emptyList());
+        mInCallController.onCallRemoved(mMockCall);
+        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+
+        // Verify that the mapping was properly removed.
+        assertNull(mInCallController.getInCallServiceConnections().get(testUser));
+    }
+
+    private void setupMocksForWorkProfileTest() {
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        when(mMockCallsManager.isInEmergencyCall()).thenReturn(false);
+        when(mMockChildUserCall.isIncoming()).thenReturn(false);
+        when(mMockChildUserCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
+        when(mDefaultDialerCache.getDefaultDialerApplication(CURRENT_USER_ID)).thenReturn(DEF_PKG);
+        when(mMockContext.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class),
+                anyInt(), any())).thenReturn(true);
+        when(mMockChildUserCall.isExternalCall()).thenReturn(false);
+        when(mMockChildUserCall.isSelfManaged()).thenReturn(true);
+        when(mMockChildUserCall.visibleToInCallService()).thenReturn(true);
+
+        //Setup up parent and child/work profile relation
+        when(mMockUserInfo.getUserHandle()).thenReturn(mParentUserHandle);
+        when(mMockChildUserInfo.getUserHandle()).thenReturn(mChildUserHandle);
+        when(mMockUserInfo.isManagedProfile()).thenReturn(false);
+        when(mMockChildUserInfo.isManagedProfile()).thenReturn(true);
+        when(mMockChildUserCall.getAssociatedUser()).thenReturn(mChildUserHandle);
+        when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mChildUserHandle);
+        when(mMockUserManager.getProfileParent(mChildUserHandle.getIdentifier())).thenReturn(
+                mMockUserInfo);
+        when(mMockUserManager.getProfileParent(mChildUserHandle)).thenReturn(mParentUserHandle);
+        when(mMockUserManager.getUserInfo(eq(mParentUserHandle.getIdentifier()))).thenReturn(
+                mMockUserInfo);
+        when(mMockUserManager.getUserInfo(eq(mChildUserHandle.getIdentifier()))).thenReturn(
+                mMockChildUserInfo);
+        when(mMockUserManager.isManagedProfile(mChildUserHandle.getIdentifier())).thenReturn(true);
+        when(mMockUserManager.isManagedProfile(mParentUserHandle.getIdentifier())).thenReturn(
+                false);
+    }
+
+    @Test
+    public void testManagedProfileCallQueriesIcsUsingParentUserToo() throws Exception {
+        setupMocksForWorkProfileTest();
+        setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
+        setupMockPackageManager(true /* default */,
+                true /*useNonUiInCalls*/, true /*useAppOpNonUiInCalls*/,
+                true /*useSystemDialer*/, false /*includeExternalCalls*/,
+                true /*includeSelfManagedCallsInDefaultDialer*/,
+                true /*includeSelfManagedCallsInCarModeDialer*/,
+                true /*includeSelfManagedCallsInNonUi*/);
+
+        //pass in call by child/work-profileuser
+        mInCallController.bindToServices(mMockChildUserCall);
+
+        // Verify that queryIntentServicesAsUser is also called with parent handle
+        // Query for the different InCallServices
+        ArgumentCaptor<Integer> userIdCaptor = ArgumentCaptor.forClass(Integer.class);
+        ArgumentCaptor<Intent> queryIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+        ArgumentCaptor<Integer> flagCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMockPackageManager, times(6)).queryIntentServicesAsUser(
+                queryIntentCaptor.capture(), flagCaptor.capture(), userIdCaptor.capture());
+        List<Integer> userIds = userIdCaptor.getAllValues();
+
+        //check if queryIntentServices was called with child user handle
+        assertTrue("no query parent user handle",
+                userIds.contains(mChildUserHandle.getIdentifier()));
+        //check if queryIntentServices was also called with parent user handle
+        assertTrue("no query parent user handle",
+                userIds.contains(mParentUserHandle.getIdentifier()));
+    }
+
     private void setupMocks(boolean isExternalCall) {
         setupMocks(isExternalCall, false /* isSelfManagedCall */);
     }
@@ -1745,6 +1910,7 @@
         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
         when(mMockCallsManager.isInEmergencyCall()).thenReturn(false);
         when(mMockCall.isIncoming()).thenReturn(false);
+        when(mMockCall.getAssociatedUser()).thenReturn(mUserHandle);
         when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
         when(mDefaultDialerCache.getDefaultDialerApplication(CURRENT_USER_ID)).thenReturn(DEF_PKG);
         when(mMockContext.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class),
diff --git a/tests/src/com/android/server/telecom/tests/InCallTonePlayerTest.java b/tests/src/com/android/server/telecom/tests/InCallTonePlayerTest.java
index f11afc1..1f1b939 100644
--- a/tests/src/com/android/server/telecom/tests/InCallTonePlayerTest.java
+++ b/tests/src/com/android/server/telecom/tests/InCallTonePlayerTest.java
@@ -35,6 +35,7 @@
 import android.media.ToneGenerator;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.server.telecom.AsyncRingtonePlayer;
 import com.android.server.telecom.CallAudioManager;
 import com.android.server.telecom.CallAudioRoutePeripheralAdapter;
 import com.android.server.telecom.CallAudioRouteStateMachine;
@@ -69,6 +70,7 @@
     @Mock private InCallTonePlayer.ToneGeneratorFactory mToneGeneratorFactory;
     @Mock private WiredHeadsetManager mWiredHeadsetManager;
     @Mock private DockManager mDockManager;
+    @Mock private AsyncRingtonePlayer mRingtonePlayer;
     @Mock private BluetoothDevice mDevice;
     @Mock private BluetoothAdapter mBluetoothAdapter;
 
@@ -124,7 +126,7 @@
 
         mCallAudioRoutePeripheralAdapter = new CallAudioRoutePeripheralAdapter(
                 mCallAudioRouteStateMachine, mBluetoothRouteManager, mWiredHeadsetManager,
-                mDockManager);
+                mDockManager, mRingtonePlayer);
         mFactory = new InCallTonePlayer.Factory(mCallAudioRoutePeripheralAdapter, mLock,
                 mToneGeneratorFactory, mMediaPlayerFactory, mAudioManagerAdapter);
         mFactory.setCallAudioManager(mCallAudioManager);
diff --git a/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java b/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java
index a38de94..914fdc5 100644
--- a/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java
+++ b/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java
@@ -75,7 +75,7 @@
 
         when(mAudioCall.getVideoState()).thenReturn(VideoProfile.STATE_AUDIO_ONLY);
         when(mAudioCall.getTargetPhoneAccountLabel()).thenReturn("Bar");
-        when(mAudioCall.getUserHandleFromTargetPhoneAccount()).
+        when(mAudioCall.getAssociatedUser()).
                 thenReturn(UserHandle.CURRENT);
         when(mVideoCall.getVideoState()).thenReturn(VideoProfile.STATE_BIDIRECTIONAL);
         when(mVideoCall.getTargetPhoneAccountLabel()).thenReturn("Bar");
@@ -84,7 +84,7 @@
         when(mRingingCall.getState()).thenReturn(CallState.RINGING);
         when(mRingingCall.getVideoState()).thenReturn(VideoProfile.STATE_AUDIO_ONLY);
         when(mRingingCall.getTargetPhoneAccountLabel()).thenReturn("Foo");
-        when(mRingingCall.getUserHandleFromTargetPhoneAccount()).
+        when(mRingingCall.getAssociatedUser()).
                 thenReturn(UserHandle.CURRENT);
         when(mRingingCall.getHandoverState()).thenReturn(HandoverState.HANDOVER_NONE);
     }
diff --git a/tests/src/com/android/server/telecom/tests/MissedInformationTest.java b/tests/src/com/android/server/telecom/tests/MissedInformationTest.java
index 8ea2739..4af3de3 100644
--- a/tests/src/com/android/server/telecom/tests/MissedInformationTest.java
+++ b/tests/src/com/android/server/telecom/tests/MissedInformationTest.java
@@ -150,7 +150,7 @@
     public void testEmergencyCallPlacing() throws Exception {
         Analytics.dumpToParcelableAnalytics();
         setUpEmergencyCall();
-        when(mEmergencyCall.getUserHandleFromTargetPhoneAccount()).
+        when(mEmergencyCall.getAssociatedUser()).
                 thenReturn(mPhoneAccountA0.getAccountHandle().getUserHandle());
         mCallsManager.addCall(mEmergencyCall);
         assertTrue(mCallsManager.isInEmergencyCall());
diff --git a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
index f2bcf18..33acd98 100644
--- a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
+++ b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
@@ -101,7 +101,7 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        when(mCall.getInitiatingUser()).thenReturn(UserHandle.CURRENT);
+        when(mCall.getAssociatedUser()).thenReturn(UserHandle.CURRENT);
         when(mCallsManager.getLock()).thenReturn(new TelecomSystem.SyncRoot() { });
         when(mCallsManager.getSystemStateHelper()).thenReturn(mSystemStateHelper);
         when(mCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
index a02415c..34360ca 100644
--- a/tests/src/com/android/server/telecom/tests/RingerTest.java
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -123,8 +124,11 @@
         when(mockRingtoneFactory.hasHapticChannels(any(Ringtone.class))).thenReturn(false);
         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());
+        when(mockCall1.getAssociatedUser()).thenReturn(PA_HANDLE.getUserHandle());
+        when(mockCall2.getAssociatedUser()).thenReturn(PA_HANDLE.getUserHandle());
+        // Set BT active state in tests to ensure that we do not end up blocking tests for 1 sec
+        // waiting for BT to connect in unit tests by default.
+        asyncRingtonePlayer.updateBtActiveState(true);
 
         createRingerUnderTest();
     }
@@ -434,6 +438,48 @@
         verify(mockRingtone).play();
     }
 
+    @SmallTest
+    @Test
+    public void testDelayRingerForBtHfpDevices() throws Exception {
+        asyncRingtonePlayer.updateBtActiveState(false);
+        Ringtone mockRingtone = ensureRingtoneMocked();
+
+        ensureRingerIsAudible();
+        assertTrue(mRingerUnderTest.startRinging(mockCall1, true));
+        assertTrue(mRingerUnderTest.isRinging());
+        // We should not have the ringtone play until BT moves active
+        verify(mockRingtone, never()).play();
+
+        asyncRingtonePlayer.updateBtActiveState(true);
+        mRingCompletionFuture.get();
+        verify(mockRingtoneFactory, times(1))
+                .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class),
+                        anyBoolean());
+        verifyNoMoreInteractions(mockRingtoneFactory);
+        verify(mockRingtone).play();
+
+        mRingerUnderTest.stopRinging();
+        verify(mockRingtone, timeout(1000/*ms*/)).stop();
+        assertFalse(mRingerUnderTest.isRinging());
+    }
+
+    @SmallTest
+    @Test
+    public void testUnblockRingerForStopCommand() throws Exception {
+        asyncRingtonePlayer.updateBtActiveState(false);
+        Ringtone mockRingtone = ensureRingtoneMocked();
+
+        ensureRingerIsAudible();
+        assertTrue(mRingerUnderTest.startRinging(mockCall1, true));
+        // We should not have the ringtone play until BT moves active
+        verify(mockRingtone, never()).play();
+
+        // We are not setting BT active, but calling stop ringing while the other thread is waiting
+        // for BT active should also unblock it.
+        mRingerUnderTest.stopRinging();
+        verify(mockRingtone, timeout(1000/*ms*/)).stop();
+    }
+
     /**
      * test shouldRingForContact will suppress the incoming call if matchesCallFilter returns
      * false (meaning DND is ON and the caller cannot bypass the settings)
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 32437e8..ed96d74 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -554,6 +554,7 @@
                     }
                 }, mDeviceIdleControllerAdapter, mAccessibilityManagerAdapter,
                 Runnable::run,
+                Runnable::run,
                 mBlockedNumbersAdapter);
 
         mComponentContextFixture.setTelecomManager(new TelecomManager(
diff --git a/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java b/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java
index c66b0f7..ddea231 100644
--- a/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java
+++ b/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java
@@ -16,6 +16,12 @@
 
 package com.android.server.telecom.tests;
 
+import static android.app.ForegroundServiceDelegationOptions.DELEGATION_SERVICE_PHONE_CALL;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
@@ -86,6 +92,31 @@
                 .thenReturn(true);
     }
 
+    /**
+     * This test ensures VoipCallMonitor is passing the correct foregroundServiceTypes when starting
+     * foreground service delegation on behalf of a client.
+     */
+    @SmallTest
+    @Test
+    public void testVerifyForegroundServiceTypesBeingPassedToActivityManager() {
+        Call call = createTestCall("testCall", mHandle1User1);
+        ArgumentCaptor<ForegroundServiceDelegationOptions> optionsCaptor =
+                ArgumentCaptor.forClass(ForegroundServiceDelegationOptions.class);
+
+        mMonitor.onCallAdded(call);
+
+        verify(mActivityManagerInternal, timeout(TIMEOUT)).startForegroundServiceDelegate(
+                 optionsCaptor.capture(), any(ServiceConnection.class));
+
+        assertEquals( FOREGROUND_SERVICE_TYPE_PHONE_CALL |
+                FOREGROUND_SERVICE_TYPE_MICROPHONE |
+                FOREGROUND_SERVICE_TYPE_CAMERA |
+                FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE,
+                optionsCaptor.getValue().mForegroundServiceTypes);
+
+        mMonitor.onCallRemoved(call);
+    }
+
     @SmallTest
     @Test
     public void testStartMonitorForOneCall() {