Merge "Move set speakerphone out of async task executor." into udc-dev
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 10a42de..113d144 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -100,7 +100,7 @@
<string name="notification_channel_background_calls" msgid="7785659903711350506">"Trucades en segon pla"</string>
<string name="notification_channel_disconnected_calls" msgid="8228636543997645757">"Trucades desconnectades"</string>
<string name="notification_channel_in_call_service_crash" msgid="7313237519166984267">"Aplicacions del telèfon que han fallat"</string>
- <string name="notification_channel_call_streaming" msgid="5100510699787538991">"Reproducció en continu de trucades"</string>
+ <string name="notification_channel_call_streaming" msgid="5100510699787538991">"Reproducció en directe de trucada"</string>
<string name="alert_outgoing_call" msgid="5319895109298927431">"En fer aquesta trucada, finalitzarà la de l\'aplicació <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
<string name="alert_redirect_outgoing_call_or_not" msgid="665409645789521636">"Tria com vols fer aquesta trucada"</string>
<string name="alert_place_outgoing_call_with_redirection" msgid="5221065030959024121">"Desvia la trucada amb <xliff:g id="OTHER_APP">%1$s</xliff:g>"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index b151222..57a3fce 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -110,7 +110,7 @@
<string name="phone_settings_number_not_in_contact_txt" msgid="2602249106007265757">"Броеви што не се наведени во „Контакти“"</string>
<string name="phone_settings_number_not_in_contact_summary_txt" msgid="963327038085718969">"Блокирани броеви што не се наведени во вашите „Контакти“"</string>
<string name="phone_settings_private_num_txt" msgid="6339272760338475619">"Приватно"</string>
- <string name="phone_settings_private_num_summary_txt" msgid="6755758240544021037">"Блокирај повикувачи со сокриен број"</string>
+ <string name="phone_settings_private_num_summary_txt" msgid="6755758240544021037">"Блокирај повикувачи со скриен број"</string>
<string name="phone_settings_payphone_txt" msgid="5003987966052543965">"Телефонска говорница"</string>
<string name="phone_settings_payphone_summary_txt" msgid="3936631076065563665">"Блокирај повици од телефонски говорници"</string>
<string name="phone_settings_unknown_txt" msgid="3577926178354772728">"Непознато"</string>
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 7b0b697..dd8e7e8 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..ff76b9e 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -497,7 +497,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.
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index 55a5df8..4a03726 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -1629,12 +1629,19 @@
mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes();
mIsMuted = initState.isMuted();
mWasOnSpeaker = false;
- mContext.registerReceiver(mMuteChangeReceiver,
- new IntentFilter(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED));
- mContext.registerReceiver(mMuteChangeReceiver,
- new IntentFilter(AudioManager.STREAM_MUTE_CHANGED_ACTION));
- mContext.registerReceiver(mSpeakerPhoneChangeReceiver,
- new IntentFilter(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED));
+ IntentFilter micMuteChangedFilter = new IntentFilter(
+ AudioManager.ACTION_MICROPHONE_MUTE_CHANGED);
+ micMuteChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ mContext.registerReceiver(mMuteChangeReceiver, micMuteChangedFilter);
+
+ IntentFilter muteChangedFilter = new IntentFilter(AudioManager.STREAM_MUTE_CHANGED_ACTION);
+ muteChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ mContext.registerReceiver(mMuteChangeReceiver, muteChangedFilter);
+
+ IntentFilter speakerChangedFilter = new IntentFilter(
+ AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED);
+ speakerChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ mContext.registerReceiver(mSpeakerPhoneChangeReceiver, speakerChangedFilter);
mStatusBarNotifier.notifyMute(initState.isMuted());
// We used to call mStatusBarNotifier.notifySpeakerphone, but that makes no sense as there
diff --git a/src/com/android/server/telecom/CallEndpointController.java b/src/com/android/server/telecom/CallEndpointController.java
index 82164b3..7e11b47 100644
--- a/src/com/android/server/telecom/CallEndpointController.java
+++ b/src/com/android/server/telecom/CallEndpointController.java
@@ -247,8 +247,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 d90524d..1323633 100644
--- a/src/com/android/server/telecom/CallStreamingController.java
+++ b/src/com/android/server/telecom/CallStreamingController.java
@@ -39,9 +39,12 @@
import android.telecom.Log;
import com.android.internal.telecom.ICallStreamingService;
+import com.android.server.telecom.voip.ParallelTransaction;
+import com.android.server.telecom.voip.SerialTransaction;
import com.android.server.telecom.voip.VoipCallTransaction;
import com.android.server.telecom.voip.VoipCallTransactionResult;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
@@ -179,7 +182,7 @@
super(mTelecomLock);
mWrapper = wrapper;
mCall = call;
- mUserHandle = mCall.getInitiatingUser();
+ mUserHandle = mCall.getAssociatedUser();
mContext = context;
}
@@ -265,6 +268,51 @@
}
}
+ public class StartStreamingTransaction extends SerialTransaction {
+ private Call mCall;
+
+ public StartStreamingTransaction(List<VoipCallTransaction> subTransactions, Call call,
+ TelecomSystem.SyncRoot lock) {
+ super(subTransactions, lock);
+ mCall = call;
+ }
+
+ @Override
+ public void handleTransactionFailure() {
+ mTransactionalServiceWrapper.stopCallStreaming(mCall);
+ }
+ }
+
+ public VoipCallTransaction getStartStreamingTransaction(CallsManager callsManager,
+ TransactionalServiceWrapper wrapper, Call call, TelecomSystem.SyncRoot lock) {
+ // start streaming transaction flow:
+ // 1. make sure there's no ongoing streaming call --> bind to EXO
+ // 2. change audio mode
+ // 3. bind to EXO
+ // If bind to EXO failed, add transaction for stop the streaming
+
+ // create list for multiple transactions
+ List<VoipCallTransaction> transactions = new ArrayList<>();
+ transactions.add(new QueryCallStreamingTransaction(callsManager));
+ transactions.add(new AudioInterceptionTransaction(call, true, lock));
+ transactions.add(getCallStreamingServiceTransaction(
+ callsManager.getContext(), wrapper, call));
+ return new StartStreamingTransaction(transactions, call, lock);
+ }
+
+ public VoipCallTransaction getStopStreamingTransaction(Call call, TelecomSystem.SyncRoot lock) {
+ // TODO: implement this
+ // Stop streaming transaction flow:
+ List<VoipCallTransaction> transactions = new ArrayList<>();
+
+ // 1. unbind to call streaming service
+ transactions.add(getUnbindStreamingServiceTransaction());
+ // 2. audio route operations
+ transactions.add(new CallStreamingController.AudioInterceptionTransaction(call,
+ false, lock));
+ return new ParallelTransaction(transactions, lock);
+ }
+
@Override
public void onCallRemoved(Call call) {
if (mStreamingCall == call) {
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index fa7c2b7..77570c3 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}.
*/
@@ -689,6 +699,7 @@
// Register BroadcastReceiver to handle enhanced call blocking feature related event.
IntentFilter intentFilter = new IntentFilter(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
intentFilter.addAction(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED);
context.registerReceiver(mReceiver, intentFilter, Context.RECEIVER_EXPORTED);
mGraphHandlerThreads = new LinkedList<>();
@@ -735,7 +746,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)) {
@@ -799,7 +810,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().
@@ -921,7 +932,7 @@
if (result.shouldAllowCall) {
incomingCall.setPostCallPackageName(
getRoleManagerAdapter().getDefaultCallScreeningApp(
- incomingCall.getUserHandleFromTargetPhoneAccount()
+ incomingCall.getAssociatedUser()
));
Log.i(this, "onCallFilteringComplete: allow call.");
@@ -1397,6 +1408,8 @@
// Required for backwards compatibility
handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
}
+ PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
+ phoneAccountHandle);
Call call = new Call(
generateNextCallId(extras),
mContext,
@@ -1413,6 +1426,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)) {
@@ -1430,18 +1452,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);
@@ -1557,7 +1572,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());
@@ -1634,6 +1656,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);
@@ -1742,7 +1766,7 @@
isConference ? participants : null,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
- null /* requestedAccountHandle */,
+ requestedAccountHandle /* targetPhoneAccountHandle */,
Call.CALL_DIRECTION_OUTGOING /* callDirection */,
false /* forceAttachToExistingConnection */,
isConference, /* isConference */
@@ -1763,7 +1787,6 @@
TelecomManager.PRESENTATION_ALLOWED);
}
}
- call.setTargetPhoneAccount(requestedAccountHandle);
}
call.initAnalytics(callingPackage, creationLogs.toString());
@@ -1797,11 +1820,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) {
@@ -2073,7 +2099,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",
@@ -2308,7 +2334,7 @@
// Find the user chosen call screening app.
String callScreeningApp =
mRoleManagerAdapter.getDefaultCallScreeningApp(
- theCall.getUserHandleFromTargetPhoneAccount());
+ theCall.getAssociatedUser());
CompletableFuture future =
new CallScreeningServiceHelper(mContext,
@@ -2472,7 +2498,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)) {
@@ -2755,7 +2781,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);
@@ -2817,7 +2843,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"));
@@ -3438,7 +3464,7 @@
} else {
if (setDefault) {
mPhoneAccountRegistrar
- .setUserSelectedOutgoingPhoneAccount(account, call.getInitiatingUser());
+ .setUserSelectedOutgoingPhoneAccount(account, call.getAssociatedUser());
}
if (mPendingAccountSelection != null) {
@@ -4144,6 +4170,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();
@@ -4236,6 +4264,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.
@@ -4279,6 +4308,7 @@
mCalls.remove(call);
shouldNotify = true;
}
+ mSelfManagedCallsBeingSetup.remove(call);
call.destroy();
updateExternalCallCanPullSupport();
@@ -4536,8 +4566,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));
}
@@ -4729,11 +4761,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;
}
/**
@@ -5181,6 +5216,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());
@@ -5830,7 +5868,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.
@@ -6064,6 +6104,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)) {
@@ -6350,7 +6393,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);
}
@@ -6480,4 +6523,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/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
old mode 100644
new mode 100755
index 5b727ab..c550ede
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -26,6 +26,7 @@
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationRequest;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -100,10 +101,17 @@
ParcelableConnection connection, Session.Info sessionInfo) {
Log.startSession(sessionInfo, LogUtils.Sessions.CSW_HANDLE_CREATE_CONNECTION_COMPLETE,
mPackageAbbreviation);
+ UserHandle callingUserHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
logIncoming("handleCreateConnectionComplete %s", callId);
+ // Check status hints image for cross user access
+ if (connection.getStatusHints() != null) {
+ Icon icon = connection.getStatusHints().getIcon();
+ connection.getStatusHints().setIcon(StatusHints.
+ validateAccountIconUserBoundary(icon, callingUserHandle));
+ }
ConnectionServiceWrapper.this
.handleCreateConnectionComplete(callId, request, connection);
@@ -504,6 +512,14 @@
Log.startSession(sessionInfo, LogUtils.Sessions.CSW_ADD_CONFERENCE_CALL,
mPackageAbbreviation);
+ UserHandle callingUserHandle = Binder.getCallingUserHandle();
+ // Check status hints image for cross user access
+ if (parcelableConference.getStatusHints() != null) {
+ Icon icon = parcelableConference.getStatusHints().getIcon();
+ parcelableConference.getStatusHints().setIcon(StatusHints.
+ validateAccountIconUserBoundary(icon, callingUserHandle));
+ }
+
if (parcelableConference.getConnectElapsedTimeMillis() != 0
&& mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
@@ -769,10 +785,17 @@
public void setStatusHints(String callId, StatusHints statusHints,
Session.Info sessionInfo) {
Log.startSession(sessionInfo, "CSW.sSH", mPackageAbbreviation);
+ UserHandle callingUserHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
logIncoming("setStatusHints %s %s", callId, statusHints);
+ // Check status hints image for cross user access
+ if (statusHints != null) {
+ Icon icon = statusHints.getIcon();
+ statusHints.setIcon(StatusHints.validateAccountIconUserBoundary(
+ icon, callingUserHandle));
+ }
Call call = mCallIdMapper.getCall(callId);
if (call != null) {
call.setStatusHints(statusHints);
@@ -1007,6 +1030,14 @@
connection.getCallDirection(),
connection.getCallerNumberVerificationStatus());
}
+
+ // Check status hints image for cross user access
+ if (connection.getStatusHints() != null) {
+ Icon icon = connection.getStatusHints().getIcon();
+ connection.getStatusHints().setIcon(StatusHints.
+ validateAccountIconUserBoundary(icon, userHandle));
+ }
+
// Check to see if this Connection has already been added.
Call alreadyAddedConnection = mCallsManager
.getAlreadyAddedConnection(connectIdToCheck);
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/DefaultDialerCache.java b/src/com/android/server/telecom/DefaultDialerCache.java
index 3ce394e..dc79715 100644
--- a/src/com/android/server/telecom/DefaultDialerCache.java
+++ b/src/com/android/server/telecom/DefaultDialerCache.java
@@ -160,6 +160,7 @@
packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
packageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
packageIntentFilter.addDataScheme("package");
+ packageIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
context.registerReceiverAsUser(mReceiver, UserHandle.ALL, packageIntentFilter, null, null);
IntentFilter bootIntentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
diff --git a/src/com/android/server/telecom/DockManager.java b/src/com/android/server/telecom/DockManager.java
index dda5711..114672e 100644
--- a/src/com/android/server/telecom/DockManager.java
+++ b/src/com/android/server/telecom/DockManager.java
@@ -76,6 +76,7 @@
// Register for misc other intent broadcasts.
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_DOCK_EVENT);
+ intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
context.registerReceiver(mReceiver, intentFilter);
}
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 3d3e3b4..d5689ae 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();
@@ -1119,7 +1255,9 @@
mSystemStateHelper.addListener(mSystemStateListener);
mClockProxy = clockProxy;
restrictPhoneCallOps();
- mContext.registerReceiver(mUserAddedReceiver, new IntentFilter(Intent.ACTION_USER_ADDED));
+ IntentFilter userAddedFilter = new IntentFilter(Intent.ACTION_USER_ADDED);
+ userAddedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ mContext.registerReceiver(mUserAddedReceiver, userAddedFilter);
}
private void restrictPhoneCallOps() {
@@ -1569,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)) {
@@ -1689,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);
@@ -1708,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,
@@ -1739,17 +1904,49 @@
IntentFilter packageChangedFilter = new IntentFilter(Intent.ACTION_PACKAGE_CHANGED);
packageChangedFilter.addDataScheme("package");
- mContext.registerReceiver(mPackageChangedReceiver, packageChangedFilter);
+ mContext.registerReceiverAsUser(mPackageChangedReceiver, UserHandle.ALL,
+ packageChangedFilter, null, null);
}
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()) {
@@ -1816,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);
}
}
@@ -1851,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,
@@ -1864,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,
@@ -1885,6 +2115,10 @@
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) {
@@ -1900,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));
}
}
}
@@ -2436,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();
}
}
}
@@ -2600,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 f5a3450..acf07e3 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -205,6 +205,7 @@
// register context based receiver to clean up orphan phone accounts
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MANAGED_PROFILE_REMOVED);
+ intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiver(mManagedProfileReceiver, intentFilter);
read();
@@ -562,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/RespondViaSmsManager.java b/src/com/android/server/telecom/RespondViaSmsManager.java
index 8507703..1d42db4 100644
--- a/src/com/android/server/telecom/RespondViaSmsManager.java
+++ b/src/com/android/server/telecom/RespondViaSmsManager.java
@@ -215,8 +215,9 @@
MessageSentReceiver receiver = new MessageSentReceiver(
!TextUtils.isEmpty(contactName) ? contactName : phoneNumber,
messageParts.size());
- context.registerReceiver(receiver, new IntentFilter(ACTION_MESSAGE_SENT),
- Context.RECEIVER_NOT_EXPORTED);
+ IntentFilter messageSentFilter = new IntentFilter(ACTION_MESSAGE_SENT);
+ messageSentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ context.registerReceiver(receiver, messageSentFilter, Context.RECEIVER_NOT_EXPORTED);
smsManager.sendMultipartTextMessage(phoneNumber, null, messageParts,
sentIntents/*sentIntent*/, null /*deliveryIntent*/, context.getOpPackageName(),
context.getAttributionTag());
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 45fb2af..1710604 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -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/SystemStateHelper.java b/src/com/android/server/telecom/SystemStateHelper.java
index dd978c2..5eed1ac 100644
--- a/src/com/android/server/telecom/SystemStateHelper.java
+++ b/src/com/android/server/telecom/SystemStateHelper.java
@@ -146,9 +146,11 @@
IntentFilter intentFilter1 = new IntentFilter(
UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED);
intentFilter1.addAction(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED);
+ intentFilter1.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
IntentFilter intentFilter2 = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
intentFilter2.addDataScheme("package");
+ intentFilter2.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiver(mBroadcastReceiver, intentFilter1);
mContext.registerReceiver(mBroadcastReceiver, intentFilter2);
Log.i(this, "Registering broadcast receiver: %s", intentFilter1);
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index ca42b57..f33b185 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);
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index d3ca0b7..67bb81f 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -116,6 +116,11 @@
.addDataAuthority(DialerCodeReceiver.TELECOM_SECRET_CODE_MARK, null);
DIALER_SECRET_CODE_FILTER
.addDataAuthority(DialerCodeReceiver.TELECOM_SECRET_CODE_MENU, null);
+
+ USER_SWITCHED_FILTER.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ USER_STARTING_FILTER.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ BOOT_COMPLETE_FILTER.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ DIALER_SECRET_CODE_FILTER.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
}
private static TelecomSystem INSTANCE = null;
diff --git a/src/com/android/server/telecom/TransactionalServiceRepository.java b/src/com/android/server/telecom/TransactionalServiceRepository.java
index f84b934..15278e1 100644
--- a/src/com/android/server/telecom/TransactionalServiceRepository.java
+++ b/src/com/android/server/telecom/TransactionalServiceRepository.java
@@ -16,6 +16,7 @@
package com.android.server.telecom;
+import android.telecom.Log;
import android.telecom.PhoneAccountHandle;
import com.android.internal.telecom.ICallEventCallback;
@@ -28,8 +29,8 @@
* more calls.
*/
public class TransactionalServiceRepository {
-
- private static final Map<PhoneAccountHandle, TransactionalServiceWrapper> lookupTable =
+ private static final String TAG = TransactionalServiceRepository.class.getSimpleName();
+ private static final Map<PhoneAccountHandle, TransactionalServiceWrapper> mServiceLookupTable =
new HashMap<>();
public TransactionalServiceRepository() {
@@ -38,12 +39,15 @@
public TransactionalServiceWrapper addNewCallForTransactionalServiceWrapper
(PhoneAccountHandle phoneAccountHandle, ICallEventCallback callEventCallback,
CallsManager callsManager, Call call) {
-
- TransactionalServiceWrapper service = null;
+ TransactionalServiceWrapper service;
+ // Only create a new TransactionalServiceWrapper if this is the first call for a package.
+ // Otherwise, get the existing TSW and add the new call to the service.
if (!hasExistingServiceWrapper(phoneAccountHandle)) {
+ Log.d(TAG, "creating a new TSW; handle=[%s]", phoneAccountHandle);
service = new TransactionalServiceWrapper(callEventCallback,
callsManager, phoneAccountHandle, call, this);
} else {
+ Log.d(TAG, "add a new call to an existing TSW; handle=[%s]", phoneAccountHandle);
service = getTransactionalServiceWrapper(phoneAccountHandle);
if (service == null) {
throw new IllegalStateException("service is null");
@@ -52,25 +56,25 @@
}
}
- lookupTable.put(phoneAccountHandle, service);
+ mServiceLookupTable.put(phoneAccountHandle, service);
return service;
}
public TransactionalServiceWrapper getTransactionalServiceWrapper(PhoneAccountHandle pah) {
- return lookupTable.get(pah);
+ return mServiceLookupTable.get(pah);
}
public boolean hasExistingServiceWrapper(PhoneAccountHandle pah) {
- return lookupTable.containsKey(pah);
+ return mServiceLookupTable.containsKey(pah);
}
public boolean removeServiceWrapper(PhoneAccountHandle pah) {
+ Log.i(TAG, "removeServiceWrapper: for phoneAccountHandle=[%s]", pah);
if (!hasExistingServiceWrapper(pah)) {
return false;
}
- lookupTable.remove(pah);
+ mServiceLookupTable.remove(pah);
return true;
}
-
}
diff --git a/src/com/android/server/telecom/TransactionalServiceWrapper.java b/src/com/android/server/telecom/TransactionalServiceWrapper.java
index ec95f39..25aaad7 100644
--- a/src/com/android/server/telecom/TransactionalServiceWrapper.java
+++ b/src/com/android/server/telecom/TransactionalServiceWrapper.java
@@ -51,17 +51,17 @@
import com.android.server.telecom.voip.VoipCallTransactionResult;
import java.util.ArrayList;
-import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Implements {@link android.telecom.CallEventCallback} and {@link android.telecom.CallControl}
* on a per-client basis which is tied to a {@link PhoneAccountHandle}
*/
public class TransactionalServiceWrapper implements
- ConnectionServiceFocusManager.ConnectionServiceFocus, IBinder.DeathRecipient {
+ ConnectionServiceFocusManager.ConnectionServiceFocus {
private static final String TAG = TransactionalServiceWrapper.class.getSimpleName();
// CallControl : Client (ex. voip app) --> Telecom
@@ -84,13 +84,25 @@
private final TransactionalServiceRepository mRepository;
private ConnectionServiceFocusManager.ConnectionServiceFocusListener mConnSvrFocusListener;
// init when constructor is called
- private final Hashtable<String, Call> mTrackedCalls = new Hashtable<>();
+ private final ConcurrentHashMap<String, Call> mTrackedCalls = new ConcurrentHashMap<>();
private final TelecomSystem.SyncRoot mLock;
private final String mPackageName;
// needs to be non-final for testing
private TransactionManager mTransactionManager;
private CallStreamingController mStreamingController;
+
+ // Each TransactionalServiceWrapper should have their own Binder.DeathRecipient to clean up
+ // any calls in the event the application crashes or is force stopped.
+ private final IBinder.DeathRecipient mAppDeathListener = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ Log.i(TAG, "binderDied: for package=[%s]; cleaning calls", mPackageName);
+ cleanupTransactionalServiceWrapper();
+ mICallEventCallback.asBinder().unlinkToDeath(this, 0);
+ }
+ };
+
public TransactionalServiceWrapper(ICallEventCallback callEventCallback,
CallsManager callsManager, PhoneAccountHandle phoneAccountHandle, Call call,
TransactionalServiceRepository repo) {
@@ -105,6 +117,7 @@
mTransactionManager = TransactionManager.getInstance();
mStreamingController = mCallsManager.getCallStreamingController();
mLock = mCallsManager.getLock();
+ setDeathRecipient(callEventCallback);
}
@VisibleForTesting
@@ -128,12 +141,6 @@
}
}
- public Call getCallById(String callId) {
- synchronized (mLock) {
- return mTrackedCalls.get(callId);
- }
- }
-
@VisibleForTesting
public boolean untrackCall(Call call) {
Call removedCall = null;
@@ -158,23 +165,12 @@
return callCount;
}
- @Override
- public void binderDied() {
- // remove all tacked calls from CallsManager && frameworks side
- for (String id : mTrackedCalls.keySet()) {
- Call call = mTrackedCalls.get(id);
- mCallsManager.markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR));
- mCallsManager.removeCall(call);
- // remove calls from Frameworks side
- if (mICallEventCallback != null) {
- try {
- mICallEventCallback.removeCallFromTransactionalServiceWrapper(call.getId());
- } catch (RemoteException e) {
- // pass
- }
- }
+ public void cleanupTransactionalServiceWrapper() {
+ for (Call call : mTrackedCalls.values()) {
+ mCallsManager.markCallAsDisconnected(call,
+ new DisconnectCause(DisconnectCause.ERROR, "process died"));
+ mCallsManager.removeCall(call); // This will clear mTrackedCalls && ClientTWS
}
- mTrackedCalls.clear();
}
/***
@@ -262,7 +258,8 @@
new HoldCallTransaction(mCallsManager, call), callback);
break;
case START_STREAMING:
- addTransactionsToManager(createStartStreamingTransaction(call), callback);
+ addTransactionsToManager(mStreamingController.getStartStreamingTransaction(mCallsManager,
+ TransactionalServiceWrapper.this, call, mLock), callback);
break;
}
} else {
@@ -336,7 +333,7 @@
}
};
- private void addTransactionsToManager(VoipCallTransaction transaction,
+ public void addTransactionsToManager(VoipCallTransaction transaction,
ResultReceiver callback) {
Log.d(TAG, "addTransactionsToManager");
@@ -555,10 +552,10 @@
try {
// remove the call from frameworks wrapper (client side)
mICallEventCallback.removeCallFromTransactionalServiceWrapper(call.getId());
- // remove the call from this class/wrapper (server side)
- untrackCall(call);
} catch (RemoteException e) {
}
+ // remove the call from this class/wrapper (server side)
+ untrackCall(call);
}
}
@@ -604,6 +601,15 @@
return new SerialTransaction(transactions, mLock);
}
+ private void setDeathRecipient(ICallEventCallback callEventCallback) {
+ try {
+ callEventCallback.asBinder().linkToDeath(mAppDeathListener, 0);
+ } catch (Exception e) {
+ Log.w(TAG, "setDeathRecipient: hit exception=[%s] trying to link binder to death",
+ e.toString());
+ }
+ }
+
/***
*********************************************************************************************
** FocusManager **
@@ -642,49 +648,11 @@
*********************************************************************************************
*/
- private SerialTransaction createStartStreamingTransaction(Call call) {
- // start streaming transaction flow:
- // make sure there's no ongoing streaming call --> bind to EXO
- // `-> change audio mode
- // create list for multiple transactions
- List<VoipCallTransaction> transactions = new ArrayList<>();
-
- // add t1. make sure no ongoing streaming call
- transactions.add(new CallStreamingController.QueryCallStreamingTransaction(mCallsManager));
-
- // create list for parallel transactions
- List<VoipCallTransaction> subTransactions = new ArrayList<>();
- // add t2.1 bind to call streaming service
- subTransactions.add(mStreamingController.getCallStreamingServiceTransaction(
- mCallsManager.getContext(), this, call));
- // add t2.2 audio route operations
- subTransactions.add(new CallStreamingController.AudioInterceptionTransaction(call,
- true, mLock));
-
- // add t2
- transactions.add(new ParallelTransaction(subTransactions, mLock));
- // send off to Transaction Manager to process
- return new SerialTransaction(transactions, mLock);
- }
-
- private VoipCallTransaction createStopStreamingTransaction(Call call) {
- // TODO: implement this
- // Stop streaming transaction flow:
- List<VoipCallTransaction> transactions = new ArrayList<>();
-
- // 1. unbind to call streaming service
- transactions.add(mStreamingController.getUnbindStreamingServiceTransaction());
- // 2. audio route operations
- transactions.add(new CallStreamingController.AudioInterceptionTransaction(call,
- false, mLock));
- return new ParallelTransaction(transactions, mLock);
- }
-
-
public void stopCallStreaming(Call call) {
Log.i(this, "stopCallStreaming; callid=%s", call.getId());
if (call != null && call.isStreaming()) {
- VoipCallTransaction stopStreamingTransaction = createStopStreamingTransaction(call);
+ VoipCallTransaction stopStreamingTransaction = mStreamingController
+ .getStopStreamingTransaction(call, mLock);
addTransactionsToManager(stopStreamingTransaction, new ResultReceiver(null));
}
}
diff --git a/src/com/android/server/telecom/TtyManager.java b/src/com/android/server/telecom/TtyManager.java
index 10e3348..53e917d 100644
--- a/src/com/android/server/telecom/TtyManager.java
+++ b/src/com/android/server/telecom/TtyManager.java
@@ -50,6 +50,7 @@
IntentFilter intentFilter = new IntentFilter(
TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
+ intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiver(mReceiver, intentFilter,
android.Manifest.permission.MODIFY_PHONE_STATE,
null, Context.RECEIVER_EXPORTED);
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index e5fe971..20af7b5 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -46,6 +46,7 @@
INTENT_FILTER.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
INTENT_FILTER.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
INTENT_FILTER.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
+ INTENT_FILTER.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
}
// If not in a call, BSR won't listen to the Bluetooth stack's HFP on/off messages, since
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/settings/BlockedNumbersActivity.java b/src/com/android/server/telecom/settings/BlockedNumbersActivity.java
index 7345a67..5fa5f06 100644
--- a/src/com/android/server/telecom/settings/BlockedNumbersActivity.java
+++ b/src/com/android/server/telecom/settings/BlockedNumbersActivity.java
@@ -154,8 +154,10 @@
updateButterBar();
}
};
- registerReceiver(mBlockingStatusReceiver, new IntentFilter(
- BlockedNumberContract.SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED),
+ IntentFilter blockStatusIntentFilter = new IntentFilter(
+ BlockedNumberContract.SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED);
+ blockStatusIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ registerReceiver(mBlockingStatusReceiver, blockStatusIntentFilter,
Context.RECEIVER_EXPORTED);
getLoaderManager().initLoader(0, null, this);
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/ui/NotificationChannelManager.java b/src/com/android/server/telecom/ui/NotificationChannelManager.java
index a0baa03..b3cb2c3 100644
--- a/src/com/android/server/telecom/ui/NotificationChannelManager.java
+++ b/src/com/android/server/telecom/ui/NotificationChannelManager.java
@@ -51,8 +51,9 @@
};
public void createChannels(Context context) {
- context.registerReceiver(mLocaleChangeReceiver,
- new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
+ IntentFilter localeChangedfilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
+ localeChangedfilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ context.registerReceiver(mLocaleChangeReceiver, localeChangedfilter);
createOrUpdateAll(context);
}
diff --git a/src/com/android/server/telecom/voip/SerialTransaction.java b/src/com/android/server/telecom/voip/SerialTransaction.java
index 5d5d1f3..b35b471 100644
--- a/src/com/android/server/telecom/voip/SerialTransaction.java
+++ b/src/com/android/server/telecom/voip/SerialTransaction.java
@@ -60,6 +60,7 @@
public void onTransactionCompleted(VoipCallTransactionResult result,
String transactionName) {
if (result.getResult() != VoipCallTransactionResult.RESULT_SUCCEED) {
+ handleTransactionFailure();
CompletableFuture.completedFuture(null).thenApplyAsync(
(x) -> {
VoipCallTransactionResult mainResult =
@@ -88,6 +89,7 @@
@Override
public void onTransactionTimeout(String transactionName) {
+ handleTransactionFailure();
CompletableFuture.completedFuture(null).thenApplyAsync(
(x) -> {
VoipCallTransactionResult mainResult =
@@ -111,4 +113,6 @@
scheduleTransaction();
}
}
+
+ public void handleTransactionFailure() {}
}
diff --git a/src/com/android/server/telecom/voip/VoipCallMonitor.java b/src/com/android/server/telecom/voip/VoipCallMonitor.java
index 9254395..3779a6d 100644
--- a/src/com/android/server/telecom/voip/VoipCallMonitor.java
+++ b/src/com/android/server/telecom/voip/VoipCallMonitor.java
@@ -36,6 +36,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.telecom.Call;
+
import com.android.server.telecom.CallsManagerListenerBase;
import com.android.server.telecom.LogUtils;
import com.android.server.telecom.LoggedHandlerExecutor;
@@ -46,6 +47,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -89,13 +91,21 @@
boolean sbnMatched = false;
for (Call call : mNotificationPendingCalls) {
if (info.matchesCall(call)) {
+ Log.i(this, "onNotificationPosted: found a pending "
+ + "callId=[%s] for the call notification w/ "
+ + "id=[%s]",
+ call.getId(), sbn.getId());
mNotificationPendingCalls.remove(call);
mNotificationInfoToCallMap.put(info, call);
sbnMatched = true;
break;
}
}
- if (!sbnMatched) {
+ if (!sbnMatched &&
+ !mCachedNotifications.contains(info) /* don't re-add if update */) {
+ Log.i(this, "onNotificationPosted: could not find a"
+ + "call for the call notification w/ id=[%s]",
+ sbn.getId());
// notification may post before we started to monitor the call, cache
// this notification and try to match it later with new added call.
mCachedNotifications.add(info);
@@ -154,7 +164,6 @@
Set<Call> callList = mAccountHandleToCallMap.computeIfAbsent(phoneAccountHandle,
k -> new HashSet<>());
callList.add(call);
-
CompletableFuture.completedFuture(null).thenComposeAsync(
(x) -> {
startFGSDelegation(call.getCallingPackageIdentity().mCallingPackagePid,
@@ -223,11 +232,15 @@
Log.i(this, "stopFGSDelegation of call %s", call);
PhoneAccountHandle handle = call.getTargetPhoneAccount();
Set<Call> calls = mAccountHandleToCallMap.get(handle);
+
+ // Every call for the package that is losing foreground service delegation should be
+ // removed from tracking maps/contains in this class
if (calls != null) {
for (Call c : calls) {
- stopMonitorWorks(c);
+ stopMonitorWorks(c); // remove the call from tacking in this class
}
}
+
mAccountHandleToCallMap.remove(handle);
if (mActivityManagerInternal != null) {
@@ -253,6 +266,8 @@
boolean sbnMatched = false;
for (NotificationInfo info : mCachedNotifications) {
if (info.matchesCall(call)) {
+ Log.i(this, "startMonitorNotification: found a cached call "
+ + "notification for call=[%s]", call);
mCachedNotifications.remove(info);
mNotificationInfoToCallMap.put(info, call);
sbnMatched = true;
@@ -261,6 +276,8 @@
}
if (!sbnMatched) {
// Only continue to
+ Log.i(this, "startMonitorNotification: could not find a call"
+ + " notification for the call=[%s];", call);
mNotificationPendingCalls.add(call);
CompletableFuture<Void> future = new CompletableFuture<>();
mHandler.postDelayed(() -> future.complete(null), 5000L);
@@ -288,12 +305,7 @@
mActivityManagerInternal = ami;
}
- @VisibleForTesting
- public void setNotificationListenerService(NotificationListenerService listener) {
- mNotificationListener = listener;
- }
-
- private class NotificationInfo {
+ private static class NotificationInfo extends Object {
private String mPackageName;
private UserHandle mUserHandle;
@@ -305,8 +317,49 @@
boolean matchesCall(Call call) {
PhoneAccountHandle accountHandle = call.getTargetPhoneAccount();
return mPackageName != null && mPackageName.equals(
- accountHandle.getComponentName().getPackageName())
+ accountHandle.getComponentName().getPackageName())
&& mUserHandle != null && mUserHandle.equals(accountHandle.getUserHandle());
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof NotificationInfo)) {
+ return false;
+ }
+ NotificationInfo that = (NotificationInfo) obj;
+ return Objects.equals(this.mPackageName, that.mPackageName)
+ && Objects.equals(this.mUserHandle, that.mUserHandle);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPackageName, mUserHandle);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{ NotificationInfo: [mPackageName: ")
+ .append(mPackageName)
+ .append("], [mUserHandle=")
+ .append(mUserHandle)
+ .append("] }");
+ return sb.toString();
+ }
+ }
+
+ @VisibleForTesting
+ public void postNotification(StatusBarNotification statusBarNotification) {
+ mNotificationListener.onNotificationPosted(statusBarNotification);
+ }
+
+ @VisibleForTesting
+ public void removeNotification(StatusBarNotification statusBarNotification) {
+ mNotificationListener.onNotificationRemoved(statusBarNotification);
+ }
+
+ @VisibleForTesting
+ public Set<Call> getCallsForHandle(PhoneAccountHandle handle){
+ return mAccountHandleToCallMap.get(handle);
}
}
diff --git a/testapps/transactionalVoipApp/res/layout/in_call_activity.xml b/testapps/transactionalVoipApp/res/layout/in_call_activity.xml
index 54d467e..a92a99b 100644
--- a/testapps/transactionalVoipApp/res/layout/in_call_activity.xml
+++ b/testapps/transactionalVoipApp/res/layout/in_call_activity.xml
@@ -29,6 +29,13 @@
/>
<Button
+ android:id="@+id/updateCallStyleNotification"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/update_notification"
+ />
+
+ <Button
android:id="@+id/answer_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -58,6 +65,12 @@
android:layout_height="wrap_content"
android:text="@string/start_stream"/>
+ <Button
+ android:id="@+id/crash_app"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/crash_app"/>
+
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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/res/values/strings.xml b/testapps/transactionalVoipApp/res/values/strings.xml
index 23a5118..8239a0e 100644
--- a/testapps/transactionalVoipApp/res/values/strings.xml
+++ b/testapps/transactionalVoipApp/res/values/strings.xml
@@ -38,5 +38,7 @@
<string name="request_bluetooth_endpoint">Bluetooth</string>
<!-- extra functionality -->
<string name="start_stream">start streaming</string>
+ <string name="crash_app">throw exception</string>
+ <string name="update_notification"> update notification to ongoing call style</string>
</resources>
\ No newline at end of file
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 3e53800..50556a1 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java
@@ -21,11 +21,9 @@
import static android.telecom.CallAttributes.DIRECTION_OUTGOING;
import android.app.Activity;
-import android.graphics.Color;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.MediaPlayer;
-import android.net.StringNetworkSpecifier;
import android.net.Uri;
import android.os.Bundle;
import android.os.OutcomeReceiver;
@@ -164,10 +162,35 @@
}
}
});
+
+ findViewById(R.id.crash_app).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // To test edge cases, it may be useful to crash the app. To do this, throwing a
+ // RuntimeException is sufficient.
+ throw new RuntimeException(
+ "Intentionally throwing RuntimeException from InCallActivity");
+ }
+ });
+
+ findViewById(R.id.updateCallStyleNotification).setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Utils.updateCallStyleNotification_toOngoingCall(getApplicationContext());
+ }
+ });
+ }
+
+ @Override
+ protected void onStop() {
+ Log.i(TAG, "onStop: InCallActivity has stopped");
+ super.onStop();
}
@Override
protected void onDestroy() {
+ Log.i(TAG, "onDestroy: InCallActivity has been destroyed");
disconnectAndStopAudio();
super.onDestroy();
}
@@ -205,7 +228,12 @@
sb.append("Error Getting Id");
}
sb.append("]");
- view.setText(sb.toString());
+ try {
+ view.setText(sb.toString());
+ }
+ catch (Exception e){
+ // ignore updating the ui
+ }
}
private void addCall() {
@@ -223,6 +251,7 @@
@Override
public void onResult(CallControl callControl) {
Log.i(TAG, "addCall: onResult: callback fired");
+ Utils.postIncomingCallStyleNotification(getApplicationContext());
mVoipCall.onAddCallControl(callControl);
updateCallId();
updateCurrentEndpoint();
@@ -247,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 98de790..ec448b2 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/Utils.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/Utils.java
@@ -17,6 +17,7 @@
package com.android.server.telecom.transactionalVoipApp;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Person;
import android.content.ComponentName;
@@ -38,9 +39,11 @@
public class Utils {
public static final String TAG = "TransactionalAppUtils";
+ public static final String CALLER_NAME = "Sundar Pichai";
public static final String sEXTRAS_KEY = "ExtrasKey";
public static final String sCALL_DIRECTION_KEY = "CallDirectionKey";
public static final String CHANNEL_ID = "TelecomVoipAppChannelId";
+ public static final int CALL_NOTIFICATION_ID = 123456;
private static final int SAMPLING_RATE_HZ = 44100;
public static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE = new PhoneAccountHandle(
@@ -70,15 +73,52 @@
.setContentTitle("Incoming call")
.setSmallIcon(R.drawable.ic_android_black_24dp)
.setStyle(Notification.CallStyle.forIncomingCall(
- new Person.Builder().setName("Tom Stu").setImportant(true).build(),
+ new Person.Builder().setName(CALLER_NAME).setImportant(true).build(),
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,
+ new Intent(""), PendingIntent.FLAG_IMMUTABLE);
+
+ Notification callStyleNotification = new Notification.Builder(context,
+ CHANNEL_ID)
+ .setContentText("active call in the TransactionalTestApp")
+ .setContentTitle("Ongoing call")
+ .setSmallIcon(R.drawable.ic_android_black_24dp)
+ .setStyle(Notification.CallStyle.forOngoingCall(
+ new Person.Builder().setName(CALLER_NAME).setImportant(true).build(),
+ ongoingCall)
+ )
+ .setFullScreenIntent(ongoingCall, true)
+ .setOngoing(true)
+ .build();
+
+ NotificationManager notificationManager =
+ context.getSystemService(NotificationManager.class);
+
+ 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 ae7d9d0..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(123456,
- 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/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index 9047da3..bd81a2f 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -16,8 +16,11 @@
package com.android.server.telecom.tests;
+import static com.android.server.telecom.tests.ConnectionServiceFixture.STATUS_HINTS_EXTRA;
+
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.mockito.ArgumentMatchers.nullable;
@@ -38,10 +41,14 @@
import android.content.IContentProvider;
import android.content.pm.PackageManager;
import android.media.AudioDeviceInfo;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -55,12 +62,14 @@
import android.telecom.ParcelableCall;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
+import android.telecom.StatusHints;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
import com.android.internal.telecom.IInCallAdapter;
import android.telecom.CallerInfo;
@@ -196,7 +205,7 @@
@Test
public void testTelecomManagerAcceptRingingVideoCall() throws Exception {
IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
- VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+ VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA, null);
assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
@@ -225,7 +234,7 @@
@Test
public void testTelecomManagerAcceptRingingVideoCallAsAudio() throws Exception {
IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
- VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+ VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA, null);
assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
@@ -256,7 +265,7 @@
@Test
public void testTelecomManagerAcceptRingingInvalidVideoState() throws Exception {
IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
- VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+ VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA, null);
assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
@@ -694,13 +703,13 @@
@MediumTest
@Test
public void testBasicConferenceCall() throws Exception {
- makeConferenceCall();
+ makeConferenceCall(null, null);
}
@MediumTest
@Test
public void testAddCallToConference1() throws Exception {
- ParcelableCall conferenceCall = makeConferenceCall();
+ ParcelableCall conferenceCall = makeConferenceCall(null, null);
IdPair callId3 = startAndMakeActiveOutgoingCall("650-555-1214",
mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
// testAddCallToConference{1,2} differ in the order of arguments to InCallAdapter#conference
@@ -718,7 +727,7 @@
@MediumTest
@Test
public void testAddCallToConference2() throws Exception {
- ParcelableCall conferenceCall = makeConferenceCall();
+ ParcelableCall conferenceCall = makeConferenceCall(null, null);
IdPair callId3 = startAndMakeActiveOutgoingCall("650-555-1214",
mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
mInCallServiceFixtureX.getInCallAdapter()
@@ -976,7 +985,7 @@
public void testOutgoingCallSelectPhoneAccountVideo() throws Exception {
startOutgoingPhoneCallPendingCreateConnection("650-555-1212",
null, mConnectionServiceFixtureA,
- Process.myUserHandle(), VideoProfile.STATE_BIDIRECTIONAL);
+ Process.myUserHandle(), VideoProfile.STATE_BIDIRECTIONAL, null);
com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
.iterator().next();
assert(call.isVideoCallingSupportedByPhoneAccount());
@@ -999,7 +1008,7 @@
public void testOutgoingCallSelectPhoneAccountNoVideo() throws Exception {
startOutgoingPhoneCallPendingCreateConnection("650-555-1212",
null, mConnectionServiceFixtureA,
- Process.myUserHandle(), VideoProfile.STATE_BIDIRECTIONAL);
+ Process.myUserHandle(), VideoProfile.STATE_BIDIRECTIONAL, null);
com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
.iterator().next();
assert(call.isVideoCallingSupportedByPhoneAccount());
@@ -1214,4 +1223,145 @@
assertTrue(muteValues.get(0));
assertFalse(muteValues.get(1));
}
+
+ /**
+ * Verifies that StatusHints image is validated in ConnectionServiceWrapper#addConferenceCall
+ * when the image doesn't belong to the calling user. Simulates a scenario where an app
+ * could manipulate the contents of the bundle and send it via the binder to upload an image
+ * from another user.
+ *
+ * @throws Exception
+ */
+ @SmallTest
+ @Test
+ public void testValidateStatusHintsImage_addConferenceCall() throws Exception {
+ Intent callIntent1 = new Intent();
+ // Stub intent for call2
+ Intent callIntent2 = new Intent();
+ Bundle callExtras1 = new Bundle();
+ Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+ // Load StatusHints extra into TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS to be processed
+ // as the call extras. This will be leveraged in ConnectionServiceFixture to set the
+ // StatusHints for the given connection.
+ StatusHints statusHints = new StatusHints(icon);
+ assertNotNull(statusHints.getIcon());
+ callExtras1.putParcelable(STATUS_HINTS_EXTRA, statusHints);
+ callIntent1.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras1);
+
+ // Start conference call to invoke ConnectionServiceWrapper#addConferenceCall.
+ // Note that the calling user would be User 0.
+ ParcelableCall conferenceCall = makeConferenceCall(callIntent1, callIntent2);
+
+ // Ensure that StatusHints was set.
+ assertNotNull(mInCallServiceFixtureX.getCall(mInCallServiceFixtureX.mLatestCallId)
+ .getStatusHints());
+ // Ensure that the StatusHints image icon was disregarded.
+ assertNull(mInCallServiceFixtureX.getCall(mInCallServiceFixtureX.mLatestCallId)
+ .getStatusHints().getIcon());
+ }
+
+ /**
+ * Verifies that StatusHints image is validated in
+ * ConnectionServiceWrapper#handleCreateConnectionComplete when the image doesn't belong to the
+ * calling user. Simulates a scenario where an app could manipulate the contents of the
+ * bundle and send it via the binder to upload an image from another user.
+ *
+ * @throws Exception
+ */
+ @SmallTest
+ @Test
+ public void testValidateStatusHintsImage_handleCreateConnectionComplete() throws Exception {
+ Bundle extras = new Bundle();
+ Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+ // Load the bundle with the test extra in order to simulate an app directly invoking the
+ // binder on ConnectionServiceWrapper#handleCreateConnectionComplete.
+ StatusHints statusHints = new StatusHints(icon);
+ assertNotNull(statusHints.getIcon());
+ extras.putParcelable(STATUS_HINTS_EXTRA, statusHints);
+
+ // Start incoming call with StatusHints extras
+ // Note that the calling user in ConnectionServiceWrapper#handleCreateConnectionComplete
+ // would be User 0.
+ IdPair ids = startIncomingPhoneCallWithExtras("650-555-1212",
+ mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA, extras);
+
+ // Ensure that StatusHints was set.
+ assertNotNull(mInCallServiceFixtureX.getCall(ids.mCallId).getStatusHints());
+ // Ensure that the StatusHints image icon was disregarded.
+ assertNull(mInCallServiceFixtureX.getCall(ids.mCallId).getStatusHints().getIcon());
+ }
+
+ /**
+ * Verifies that StatusHints image is validated in ConnectionServiceWrapper#setStatusHints
+ * when the image doesn't belong to the calling user. Simulates a scenario where an app
+ * could manipulate the contents of the bundle and send it via the binder to upload an image
+ * from another user.
+ *
+ * @throws Exception
+ */
+ @SmallTest
+ @Test
+ public void testValidateStatusHintsImage_setStatusHints() throws Exception {
+ IdPair outgoing = startAndMakeActiveOutgoingCall("650-555-1214",
+ mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
+
+ // Modify existing connection with StatusHints image exploit
+ Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+ StatusHints statusHints = new StatusHints(icon);
+ assertNotNull(statusHints.getIcon());
+ ConnectionServiceFixture.ConnectionInfo connectionInfo = mConnectionServiceFixtureA
+ .mConnectionById.get(outgoing.mConnectionId);
+ connectionInfo.statusHints = statusHints;
+
+ // Invoke ConnectionServiceWrapper#setStatusHints.
+ // Note that the calling user would be User 0.
+ mConnectionServiceFixtureA.sendSetStatusHints(outgoing.mConnectionId);
+ waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+ TEST_TIMEOUT);
+
+ // Ensure that StatusHints was set.
+ assertNotNull(mInCallServiceFixtureX.getCall(outgoing.mCallId).getStatusHints());
+ // Ensure that the StatusHints image icon was disregarded.
+ assertNull(mInCallServiceFixtureX.getCall(outgoing.mCallId)
+ .getStatusHints().getIcon());
+ }
+
+ /**
+ * Verifies that StatusHints image is validated in
+ * ConnectionServiceWrapper#addExistingConnection when the image doesn't belong to the calling
+ * user. Simulates a scenario where an app could manipulate the contents of the bundle and
+ * send it via the binder to upload an image from another user.
+ *
+ * @throws Exception
+ */
+ @SmallTest
+ @Test
+ public void testValidateStatusHintsImage_addExistingConnection() throws Exception {
+ IdPair outgoing = startAndMakeActiveOutgoingCall("650-555-1214",
+ mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
+ Connection existingConnection = mConnectionServiceFixtureA.mLatestConnection;
+
+ // Modify existing connection with StatusHints image exploit
+ Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+ StatusHints modifiedStatusHints = new StatusHints(icon);
+ assertNotNull(modifiedStatusHints.getIcon());
+ ConnectionServiceFixture.ConnectionInfo connectionInfo = mConnectionServiceFixtureA
+ .mConnectionById.get(outgoing.mConnectionId);
+ connectionInfo.statusHints = modifiedStatusHints;
+
+ // Invoke ConnectionServiceWrapper#addExistingConnection.
+ // Note that the calling user would be User 0.
+ mConnectionServiceFixtureA.sendAddExistingConnection(outgoing.mConnectionId);
+ waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+ TEST_TIMEOUT);
+
+ // Ensure that StatusHints was set. Due to test setup, the ParcelableConnection object that
+ // is passed into sendAddExistingConnection is instantiated on invocation. The call's
+ // StatusHints are not updated at the time of completion, so instead, we can verify that
+ // the ParcelableConnection object was modified.
+ assertNotNull(mConnectionServiceFixtureA.mLatestParcelableConnection.getStatusHints());
+ // Ensure that the StatusHints image icon was disregarded.
+ assertNull(mConnectionServiceFixtureA.mLatestParcelableConnection
+ .getStatusHints().getIcon());
+ }
}
diff --git a/tests/src/com/android/server/telecom/tests/CallExtrasTest.java b/tests/src/com/android/server/telecom/tests/CallExtrasTest.java
index 926d740..cf44cfe 100644
--- a/tests/src/com/android/server/telecom/tests/CallExtrasTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallExtrasTest.java
@@ -370,7 +370,7 @@
@LargeTest
@Test
public void testConferenceSetExtras() throws Exception {
- ParcelableCall call = makeConferenceCall();
+ ParcelableCall call = makeConferenceCall(null, null);
String conferenceId = call.getId();
Conference conference = mConnectionServiceFixtureA.mLatestConference;
@@ -414,7 +414,7 @@
@FlakyTest(bugId = 117751305)
@Test
public void testConferenceExtraOperations() throws Exception {
- ParcelableCall call = makeConferenceCall();
+ ParcelableCall call = makeConferenceCall(null, null);
String conferenceId = call.getId();
Conference conference = mConnectionServiceFixtureA.mLatestConference;
assertNotNull(conference);
@@ -450,7 +450,7 @@
@LargeTest
@Test
public void testConferenceICS() throws Exception {
- ParcelableCall call = makeConferenceCall();
+ ParcelableCall call = makeConferenceCall(null, null);
String conferenceId = call.getId();
Conference conference = mConnectionServiceFixtureA.mLatestConference;
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 8a7d22c..9f46336 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;
@@ -156,6 +155,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(
@@ -173,6 +174,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
@@ -202,6 +207,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");
@@ -338,6 +356,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);
}
@@ -2473,7 +2493,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);
@@ -2482,7 +2502,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(),
@@ -2519,6 +2562,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();
@@ -3078,6 +3145,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 c8da78c..0927b80 100755
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
@@ -69,6 +69,9 @@
static int INVALID_VIDEO_STATE = -1;
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
@@ -103,6 +106,11 @@
if (mProperties != NOT_SPECIFIED) {
fakeConnection.setConnectionProperties(mProperties);
}
+ // Testing for StatusHints image icon cross user access
+ if (request.getExtras() != null) {
+ fakeConnection.setStatusHints(
+ request.getExtras().getParcelable(STATUS_HINTS_EXTRA));
+ }
return fakeConnection;
}
@@ -119,6 +127,11 @@
if (mProperties != NOT_SPECIFIED) {
fakeConnection.setConnectionProperties(mProperties);
}
+ // Testing for StatusHints image icon cross user access
+ if (request.getExtras() != null) {
+ fakeConnection.setStatusHints(
+ request.getExtras().getParcelable(STATUS_HINTS_EXTRA));
+ }
return fakeConnection;
}
@@ -135,6 +148,12 @@
Conference fakeConference = new FakeConference();
fakeConference.addConnection(cxn1);
fakeConference.addConnection(cxn2);
+ if (cxn1.getStatusHints() != null || cxn2.getStatusHints() != null) {
+ // For testing purposes, pick one of the status hints that isn't null.
+ StatusHints statusHints = cxn1.getStatusHints() != null
+ ? cxn1.getStatusHints() : cxn2.getStatusHints();
+ fakeConference.setStatusHints(statusHints);
+ }
mLatestConference = fakeConference;
addConference(fakeConference);
} else {
@@ -179,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
@@ -507,6 +526,7 @@
public String mLatestConnectionId;
public Connection mLatestConnection;
+ public ParcelableConnection mLatestParcelableConnection;
public Conference mLatestConference;
public final Set<IConnectionServiceAdapter> mConnectionServiceAdapters = new HashSet<>();
public final Map<String, ConnectionInfo> mConnectionById = new HashMap<>();
@@ -751,7 +771,7 @@
}
private ParcelableConnection parcelable(ConnectionInfo c) {
- return new ParcelableConnection(
+ mLatestParcelableConnection = new ParcelableConnection(
c.request.getAccountHandle(),
c.state,
c.capabilities,
@@ -772,5 +792,6 @@
c.conferenceableConnectionIds,
c.extras,
c.callerNumberVerificationStatus);
+ return mLatestParcelableConnection;
}
}
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 16fd630..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);
@@ -233,8 +241,8 @@
doAnswer(invocation -> {
mRegisteredReceiver = invocation.getArgument(0);
return null;
- }).when(mMockContext).registerReceiver(any(BroadcastReceiver.class),
- any(IntentFilter.class));
+ }).when(mMockContext).registerReceiverAsUser(any(BroadcastReceiver.class),
+ any(), any(IntentFilter.class), any(), any());
ArgumentCaptor<SystemStateHelper.SystemStateListener> systemStateListenerArgumentCaptor
= ArgumentCaptor.forClass(SystemStateHelper.SystemStateListener.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/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..a4adf77 100644
--- a/tests/src/com/android/server/telecom/tests/RingerTest.java
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -123,8 +123,8 @@
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());
createRingerUnderTest();
}
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index d013fae..b962b2a 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -428,12 +428,13 @@
super.tearDown();
}
- protected ParcelableCall makeConferenceCall() throws Exception {
- IdPair callId1 = startAndMakeActiveOutgoingCall("650-555-1212",
- mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
+ protected ParcelableCall makeConferenceCall(
+ Intent callIntentExtras1, Intent callIntentExtras2) throws Exception {
+ IdPair callId1 = startAndMakeActiveOutgoingCallWithExtras("650-555-1212",
+ mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA, callIntentExtras1);
- IdPair callId2 = startAndMakeActiveOutgoingCall("650-555-1213",
- mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
+ IdPair callId2 = startAndMakeActiveOutgoingCallWithExtras("650-555-1213",
+ mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA, callIntentExtras2);
IInCallAdapter inCallAdapter = mInCallServiceFixtureX.getInCallAdapter();
inCallAdapter.conference(callId1.mCallId, callId2.mCallId);
@@ -640,7 +641,7 @@
startOutgoingPhoneCallWaitForBroadcaster(number, null,
connectionServiceFixture, Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY,
- false /*isEmergency*/);
+ false /*isEmergency*/, null);
return mInCallServiceFixtureX.mLatestCallId;
}
@@ -670,17 +671,17 @@
throws Exception {
return startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
- initiatingUser, VideoProfile.STATE_AUDIO_ONLY);
+ initiatingUser, VideoProfile.STATE_AUDIO_ONLY, null);
}
protected IdPair startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle,
ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
- int videoState) throws Exception {
+ int videoState, Intent callIntentExtras) throws Exception {
int startingNumConnections = connectionServiceFixture.mConnectionById.size();
int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
startOutgoingPhoneCallPendingCreateConnection(number, phoneAccountHandle,
- connectionServiceFixture, initiatingUser, videoState);
+ connectionServiceFixture, initiatingUser, videoState, callIntentExtras);
verify(connectionServiceFixture.getTestDouble(), timeout(TEST_TIMEOUT))
.createConnectionComplete(anyString(), any());
@@ -723,7 +724,7 @@
// Call will not use the ordered broadcaster, since it is an Emergency Call
startOutgoingPhoneCallWaitForBroadcaster(number, phoneAccountHandle,
- connectionServiceFixture, initiatingUser, videoState, true /*isEmergency*/);
+ connectionServiceFixture, initiatingUser, videoState, true /*isEmergency*/, null);
return outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
phoneAccountHandle, connectionServiceFixture);
@@ -732,7 +733,7 @@
protected void startOutgoingPhoneCallWaitForBroadcaster(String number,
PhoneAccountHandle phoneAccountHandle,
ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
- int videoState, boolean isEmergency) throws Exception {
+ int videoState, boolean isEmergency, Intent actionCallIntent) throws Exception {
reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
mInCallServiceFixtureY.getTestDouble());
@@ -745,7 +746,9 @@
boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
- Intent actionCallIntent = new Intent();
+ if (actionCallIntent == null) {
+ actionCallIntent = new Intent();
+ }
actionCallIntent.setData(Uri.parse("tel:" + number));
actionCallIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
if(isEmergency) {
@@ -790,9 +793,10 @@
protected String startOutgoingPhoneCallPendingCreateConnection(String number,
PhoneAccountHandle phoneAccountHandle,
ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
- int videoState) throws Exception {
+ int videoState, Intent callIntentExtras) throws Exception {
startOutgoingPhoneCallWaitForBroadcaster(number,phoneAccountHandle,
- connectionServiceFixture, initiatingUser, videoState, false /*isEmergency*/);
+ connectionServiceFixture, initiatingUser,
+ videoState, false /*isEmergency*/, callIntentExtras);
waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
verifyAndProcessOutgoingCallBroadcast(phoneAccountHandle);
@@ -897,14 +901,24 @@
PhoneAccountHandle phoneAccountHandle,
final ConnectionServiceFixture connectionServiceFixture) throws Exception {
return startIncomingPhoneCall(number, phoneAccountHandle, VideoProfile.STATE_AUDIO_ONLY,
- connectionServiceFixture);
+ connectionServiceFixture, null);
+ }
+
+ protected IdPair startIncomingPhoneCallWithExtras(
+ String number,
+ PhoneAccountHandle phoneAccountHandle,
+ final ConnectionServiceFixture connectionServiceFixture,
+ Bundle extras) throws Exception {
+ return startIncomingPhoneCall(number, phoneAccountHandle, VideoProfile.STATE_AUDIO_ONLY,
+ connectionServiceFixture, extras);
}
protected IdPair startIncomingPhoneCall(
String number,
PhoneAccountHandle phoneAccountHandle,
int videoState,
- final ConnectionServiceFixture connectionServiceFixture) throws Exception {
+ final ConnectionServiceFixture connectionServiceFixture,
+ Bundle extras) throws Exception {
reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
mInCallServiceFixtureY.getTestDouble());
@@ -921,7 +935,9 @@
new IncomingCallAddedListener(incomingCallAddedLatch);
mTelecomSystem.getCallsManager().addListener(callAddedListener);
- Bundle extras = new Bundle();
+ if (extras == null) {
+ extras = new Bundle();
+ }
extras.putParcelable(
TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null));
@@ -1012,7 +1028,16 @@
PhoneAccountHandle phoneAccountHandle,
ConnectionServiceFixture connectionServiceFixture) throws Exception {
return startAndMakeActiveOutgoingCall(number, phoneAccountHandle, connectionServiceFixture,
- VideoProfile.STATE_AUDIO_ONLY);
+ VideoProfile.STATE_AUDIO_ONLY, null);
+ }
+
+ protected IdPair startAndMakeActiveOutgoingCallWithExtras(
+ String number,
+ PhoneAccountHandle phoneAccountHandle,
+ ConnectionServiceFixture connectionServiceFixture,
+ Intent callIntentExtras) throws Exception {
+ return startAndMakeActiveOutgoingCall(number, phoneAccountHandle, connectionServiceFixture,
+ VideoProfile.STATE_AUDIO_ONLY, callIntentExtras);
}
// A simple outgoing call, verifying that the appropriate connection service is contacted,
@@ -1020,9 +1045,10 @@
protected IdPair startAndMakeActiveOutgoingCall(
String number,
PhoneAccountHandle phoneAccountHandle,
- ConnectionServiceFixture connectionServiceFixture, int videoState) throws Exception {
+ ConnectionServiceFixture connectionServiceFixture, int videoState,
+ Intent callIntentExtras) throws Exception {
IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
- Process.myUserHandle(), videoState);
+ Process.myUserHandle(), videoState, callIntentExtras);
connectionServiceFixture.sendSetDialing(ids.mConnectionId);
if (phoneAccountHandle != mPhoneAccountSelfManaged.getAccountHandle()) {
@@ -1131,7 +1157,7 @@
PhoneAccountHandle phoneAccountHandle,
ConnectionServiceFixture connectionServiceFixture) throws Exception {
IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
- Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY);
+ Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY, null);
connectionServiceFixture.sendSetDialing(ids.mConnectionId);
if (phoneAccountHandle != mPhoneAccountSelfManaged.getAccountHandle()) {
diff --git a/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java b/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java
index 98624d4..fa5f2a2 100644
--- a/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java
+++ b/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java
@@ -25,6 +25,7 @@
import android.content.ComponentName;
+import android.os.IBinder;
import android.os.OutcomeReceiver;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -69,6 +70,7 @@
@Mock private TransactionManager mTransactionManager;
@Mock private ICallEventCallback mCallEventCallback;
@Mock private TransactionalServiceRepository mRepository;
+ @Mock private IBinder mIBinder;
private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() {};
@Override
@@ -79,7 +81,7 @@
Mockito.when(mMockCall1.getId()).thenReturn(CALL_ID_1);
Mockito.when(mMockCall2.getId()).thenReturn(CALL_ID_2);
Mockito.when(mCallsManager.getLock()).thenReturn(mLock);
-
+ Mockito.when(mCallEventCallback.asBinder()).thenReturn(mIBinder);
mTransactionalServiceWrapper = new TransactionalServiceWrapper(mCallEventCallback,
mCallsManager, SERVICE_HANDLE, mMockCall1, mRepository);
diff --git a/tests/src/com/android/server/telecom/tests/VideoCallTests.java b/tests/src/com/android/server/telecom/tests/VideoCallTests.java
index 97e71d1..84beedc 100644
--- a/tests/src/com/android/server/telecom/tests/VideoCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/VideoCallTests.java
@@ -105,7 +105,7 @@
// Start an incoming video call.
IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA,
- VideoProfile.STATE_BIDIRECTIONAL);
+ VideoProfile.STATE_BIDIRECTIONAL, null);
verifyAudioRoute(CallAudioState.ROUTE_SPEAKER);
}
@@ -121,7 +121,7 @@
// Start an incoming video call.
IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA,
- VideoProfile.STATE_TX_ENABLED);
+ VideoProfile.STATE_TX_ENABLED, null);
verifyAudioRoute(CallAudioState.ROUTE_SPEAKER);
}
@@ -137,7 +137,7 @@
// Start an incoming video call.
IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA,
- VideoProfile.STATE_AUDIO_ONLY);
+ VideoProfile.STATE_AUDIO_ONLY, null);
verifyAudioRoute(CallAudioState.ROUTE_EARPIECE);
}
@@ -165,7 +165,7 @@
@Test
public void testIncomingVideoCallMissedCheckVideoHistory() throws Exception {
IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
- VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+ VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA, null);
com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
.iterator().next();
@@ -182,7 +182,7 @@
@Test
public void testIncomingVideoCallRejectedCheckVideoHistory() throws Exception {
IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
- VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+ VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA, null);
com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
.iterator().next();
@@ -201,7 +201,7 @@
public void testOutgoingVideoCallCanceledCheckVideoHistory() throws Exception {
IdPair ids = startOutgoingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
mConnectionServiceFixtureA, Process.myUserHandle(),
- VideoProfile.STATE_BIDIRECTIONAL);
+ VideoProfile.STATE_BIDIRECTIONAL, null);
com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
.iterator().next();
@@ -219,7 +219,7 @@
public void testOutgoingVideoCallRejectedCheckVideoHistory() throws Exception {
IdPair ids = startOutgoingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
mConnectionServiceFixtureA, Process.myUserHandle(),
- VideoProfile.STATE_BIDIRECTIONAL);
+ VideoProfile.STATE_BIDIRECTIONAL, null);
com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
.iterator().next();
@@ -237,7 +237,7 @@
public void testOutgoingVideoCallAnsweredAsAudio() throws Exception {
IdPair ids = startOutgoingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
mConnectionServiceFixtureA, Process.myUserHandle(),
- VideoProfile.STATE_BIDIRECTIONAL);
+ VideoProfile.STATE_BIDIRECTIONAL, null);
com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
.iterator().next();
diff --git a/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java b/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java
index c9ea34f..c66b0f7 100644
--- a/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java
+++ b/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java
@@ -18,30 +18,30 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
-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.when;
import android.app.ActivityManagerInternal;
import android.app.ForegroundServiceDelegationOptions;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Person;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
import android.telecom.PhoneAccountHandle;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.server.telecom.Call;
-import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.CallState;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.voip.VoipCallMonitor;
@@ -55,16 +55,18 @@
@RunWith(JUnit4.class)
public class VoipCallMonitorTest extends TelecomTestCase {
private VoipCallMonitor mMonitor;
+ private static final String NAME = "John Smith";
private static final String PKG_NAME_1 = "telecom.voip.test1";
private static final String PKG_NAME_2 = "telecom.voip.test2";
private static final String CLS_NAME = "VoipActivity";
private static final String ID_1 = "id1";
+ public static final String CHANNEL_ID = "TelecomVoipAppChannelId";
private static final UserHandle USER_HANDLE_1 = new UserHandle(1);
private static final long TIMEOUT = 5000L;
@Mock private TelecomSystem.SyncRoot mLock;
@Mock private ActivityManagerInternal mActivityManagerInternal;
- @Mock private NotificationListenerService mListenerService;
+ @Mock private IBinder mServiceConnection;
private final PhoneAccountHandle mHandle1User1 = new PhoneAccountHandle(
new ComponentName(PKG_NAME_1, CLS_NAME), ID_1, USER_HANDLE_1);
@@ -77,12 +79,11 @@
super.setUp();
mMonitor = new VoipCallMonitor(mContext, mLock);
mActivityManagerInternal = mock(ActivityManagerInternal.class);
- mListenerService = mock(NotificationListenerService.class);
mMonitor.setActivityManagerInternal(mActivityManagerInternal);
- mMonitor.setNotificationListenerService(mListenerService);
- doNothing().when(mListenerService).registerAsSystemService(eq(mContext),
- any(ComponentName.class), anyInt());
mMonitor.startMonitor();
+ when(mActivityManagerInternal.startForegroundServiceDelegate(any(
+ ForegroundServiceDelegationOptions.class), any(ServiceConnection.class)))
+ .thenReturn(true);
}
@SmallTest
@@ -206,13 +207,98 @@
.stopForegroundServiceDelegate(any(ServiceConnection.class));
}
+ /**
+ * Ensure an app loses foreground service delegation if the user dismisses the call style
+ * notification or the app removes the notification.
+ * Note: post the notification AFTER foreground service delegation is gained
+ */
+ @SmallTest
+ @Test
+ public void testStopFgsIfCallNotificationIsRemoved_PostedAfterFgsIsGained() {
+ // GIVEN
+ StatusBarNotification sbn = createStatusBarNotificationFromHandle(mHandle1User1);
+
+ // WHEN
+ // FGS is gained after the call is added to VoipCallMonitor
+ ServiceConnection c = addCallAndVerifyFgsIsGained(createTestCall("1", mHandle1User1));
+ // simulate an app posting a call style notification after FGS is gained
+ mMonitor.postNotification(sbn);
+
+ // THEN
+ // shortly after posting the notification, simulate the user dismissing it
+ mMonitor.removeNotification(sbn);
+ // FGS should be removed once the notification is removed
+ verify(mActivityManagerInternal, timeout(TIMEOUT)).stopForegroundServiceDelegate(c);
+ }
+
+ /**
+ * Ensure an app loses foreground service delegation if the user dismisses the call style
+ * notification or the app removes the notification.
+ * Note: post the notification BEFORE foreground service delegation is gained
+ */
+ @SmallTest
+ @Test
+ public void testStopFgsIfCallNotificationIsRemoved_PostedBeforeFgsIsGained() {
+ // GIVEN
+ StatusBarNotification sbn = createStatusBarNotificationFromHandle(mHandle1User1);
+
+ // WHEN
+ // an app posts a call style notification before FGS is gained
+ mMonitor.postNotification(sbn);
+ // FGS is gained after the call is added to VoipCallMonitor
+ ServiceConnection c = addCallAndVerifyFgsIsGained(createTestCall("1", mHandle1User1));
+
+ // THEN
+ // shortly after posting the notification, simulate the user dismissing it
+ mMonitor.removeNotification(sbn);
+ // FGS should be removed once the notification is removed
+ verify(mActivityManagerInternal, timeout(TIMEOUT)).stopForegroundServiceDelegate(c);
+ }
+
private Call createTestCall(String id, PhoneAccountHandle handle) {
Call call = mock(Call.class);
when(call.getTargetPhoneAccount()).thenReturn(handle);
when(call.isTransactionalCall()).thenReturn(true);
when(call.getExtras()).thenReturn(new Bundle());
when(call.getId()).thenReturn(id);
- when(call.getCallingPackageIdentity()).thenReturn( new Call.CallingPackageIdentity() );
+ when(call.getCallingPackageIdentity()).thenReturn(new Call.CallingPackageIdentity());
+ when(call.getState()).thenReturn(CallState.ACTIVE);
return call;
}
+
+ private Notification createCallStyleNotification() {
+ PendingIntent pendingOngoingIntent = PendingIntent.getActivity(mContext, 0,
+ new Intent(""), PendingIntent.FLAG_IMMUTABLE);
+
+ return new Notification.Builder(mContext,
+ CHANNEL_ID)
+ .setStyle(Notification.CallStyle.forOngoingCall(
+ new Person.Builder().setName(NAME).setImportant(true).build(),
+ pendingOngoingIntent)
+ )
+ .setFullScreenIntent(pendingOngoingIntent, true)
+ .build();
+ }
+
+ private StatusBarNotification createStatusBarNotificationFromHandle(PhoneAccountHandle handle) {
+ return new StatusBarNotification(
+ handle.getComponentName().getPackageName(), "", 0, "", 0, 0,
+ createCallStyleNotification(), handle.getUserHandle(), "", 0);
+ }
+
+ private ServiceConnection addCallAndVerifyFgsIsGained(Call call) {
+ ArgumentCaptor<ServiceConnection> captor = ArgumentCaptor.forClass(ServiceConnection.class);
+ // add the call to the VoipCallMonitor under test which will start FGS
+ mMonitor.onCallAdded(call);
+ // FGS should be granted within the timeout
+ verify(mActivityManagerInternal, timeout(TIMEOUT))
+ .startForegroundServiceDelegate(any(
+ ForegroundServiceDelegationOptions.class),
+ captor.capture());
+ // onServiceConnected must be called in order for VoipCallMonitor to start monitoring for
+ // a notification before the timeout expires
+ ServiceConnection serviceConnection = captor.getValue();
+ serviceConnection.onServiceConnected(mHandle1User1.getComponentName(), mServiceConnection);
+ return serviceConnection;
+ }
}