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() {