Merge "Fix holding behavior across self-manged ConnectionServices." into udc-dev
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 9226599..75d3416 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -108,7 +108,7 @@
<string name="phone_settings_call_blocking_txt" msgid="7311523114822507178">"কল অৱৰোধ"</string>
<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_txt" msgid="6339272760338475619">"ব্যক্তিগত"</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>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 7c07654..29fdc4a 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -75,10 +75,10 @@
<string name="blocked_numbers_butter_bar_title" msgid="582982373755950791">"Тыйым уақытша алынды"</string>
<string name="blocked_numbers_butter_bar_body" msgid="1261213114919301485">"Төтенше жағдай нөмірін терген немесе мәтіндік хабар жіберген соң, төтенше жағдай қызметтері сізге хабарласа алуы үшін тыйым алынады."</string>
<string name="blocked_numbers_butter_bar_button" msgid="2704456308072489793">"Қазір қайта қосу"</string>
- <string name="blocked_numbers_number_blocked_message" msgid="4314736791180919167">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> бөгелген"</string>
+ <string name="blocked_numbers_number_blocked_message" msgid="4314736791180919167">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> блокталған"</string>
<string name="blocked_numbers_number_unblocked_message" msgid="2933071624674945601">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> бөгеуден шығарылды"</string>
<string name="blocked_numbers_block_emergency_number_message" msgid="4198550501500893890">"Жедел қызмет нөмірін бөгеу мүмкін емес."</string>
- <string name="blocked_numbers_number_already_blocked_message" msgid="2301270825735665458">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> бұрыннан бөгелген."</string>
+ <string name="blocked_numbers_number_already_blocked_message" msgid="2301270825735665458">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> бұрыннан блокталған."</string>
<string name="toast_personal_call_msg" msgid="5817631570381795610">"Қоңырау шалу үшін жеке нөмір тергішті пайдалану"</string>
<string name="notification_incoming_call" msgid="1233481138362230894">"<xliff:g id="CALL_VIA">%1$s</xliff:g> қоңырауы: <xliff:g id="CALL_FROM">%2$s</xliff:g>"</string>
<string name="notification_incoming_video_call" msgid="5795968314037063900">"<xliff:g id="CALL_VIA">%1$s</xliff:g> бейне қоңырауы: <xliff:g id="CALL_FROM">%2$s</xliff:g>"</string>
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index e2d8489..6ae8834 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -414,6 +414,16 @@
private boolean mIsEmergencyCall;
+ /**
+ * Flag indicating if ECBM is active for the target phone account. This only applies to MT calls
+ * in the scenario of work profiles (when the profile is paused and the user has only registered
+ * a work sim). Normally, MT calls made to the work sim should be rejected when the work apps
+ * are paused. However, when the admin makes a MO ecall, ECBM should be enabled for that sim to
+ * allow non-emergency MT calls. MO calls don't apply because the phone account would be
+ * rejected from selection if the owner is not placing the call.
+ */
+ private boolean mIsInECBM;
+
// The Call is considered an emergency call for testing, but will not actually connect to
// emergency services.
private boolean mIsTestEmergencyCall;
@@ -1592,6 +1602,21 @@
}
/**
+ * @return {@code true} if the target phone account is in ECBM.
+ */
+ public boolean isInECBM() {
+ return mIsInECBM;
+ }
+
+ /**
+ * Set if the target phone account is in ECBM.
+ * @param isInEcbm {@code true} if target phone account is in ECBM, {@code false} otherwise.
+ */
+ public void setIsInECBM(boolean isInECBM) {
+ mIsInECBM = isInECBM;
+ }
+
+ /**
* @return {@code true} if the network has identified this call as an emergency call.
*/
public boolean isNetworkIdentifiedEmergencyCall() {
@@ -1682,6 +1707,11 @@
public void setTargetPhoneAccount(PhoneAccountHandle accountHandle) {
if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) {
mTargetPhoneAccountHandle = accountHandle;
+ // Update the last MO emergency call in the helper, if applicable.
+ if (isEmergencyCall() && !isIncoming()) {
+ mCallsManager.getEmergencyCallHelper().setLastOutgoingEmergencyCallPAH(
+ accountHandle);
+ }
for (Listener l : mListeners) {
l.onTargetPhoneAccountChanged(this);
}
@@ -4575,7 +4605,7 @@
throw new UnsupportedOperationException(
"Can't streaming call created by non voip apps");
}
-
+ Log.addEvent(this, LogUtils.Events.START_STREAMING);
synchronized (mLock) {
if (mIsStreaming) {
// ignore
@@ -4595,7 +4625,7 @@
// ignore
return;
}
-
+ Log.addEvent(this, LogUtils.Events.STOP_STREAMING);
mIsStreaming = false;
for (Listener listener : mListeners) {
listener.onCallStreamingStateChanged(this, false /** isStreaming */);
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 8cac314..38e6b00 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -146,6 +146,9 @@
@Override
public void onCallRemoved(Call call) {
+ if (mStreamingCall == call) {
+ mStreamingCall = null;
+ }
if (shouldIgnoreCallForAudio(call)) {
return; // Don't do audio handling for calls in a conference, or external calls.
}
@@ -238,7 +241,7 @@
makeArgsForModeStateMachine());
} else {
Log.w(LOG_TAG, "Unexpected streaming call request for call %s while call "
- + "s is streaming.", call.getId(), mStreamingCall.getId());
+ + "%s is streaming.", call.getId(), mStreamingCall.getId());
}
} else {
if (mStreamingCall == call) {
diff --git a/src/com/android/server/telecom/CallAudioModeStateMachine.java b/src/com/android/server/telecom/CallAudioModeStateMachine.java
index 3ced36d..9ad9094 100644
--- a/src/com/android/server/telecom/CallAudioModeStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioModeStateMachine.java
@@ -665,7 +665,9 @@
@Override
public void enter() {
Log.i(LOG_TAG, "Audio focus entering streaming state");
- mAudioManager.setMode(AudioManager.MODE_CALL_REDIRECT);
+ mLocalLog.log("Enter Streaming");
+ mLocalLog.log("Mode MODE_COMMUNICATION_REDIRECT");
+ mAudioManager.setMode(AudioManager.MODE_COMMUNICATION_REDIRECT);
mMostRecentMode = AudioManager.MODE_NORMAL;
mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
mCallAudioManager.getCallAudioRouteStateMachine().sendMessageWithSessionInfo(
@@ -685,7 +687,8 @@
MessageArgs args = (MessageArgs) msg.obj;
switch (msg.what) {
case NO_MORE_ACTIVE_OR_DIALING_CALLS:
- // Do nothing.
+ // Switch to either ringing, holding, or inactive
+ transitionTo(calculateProperStateFromArgs(args));
return HANDLED;
case NO_MORE_RINGING_CALLS:
// Do nothing.
diff --git a/src/com/android/server/telecom/CallEndpointController.java b/src/com/android/server/telecom/CallEndpointController.java
index 60827e2..82164b3 100644
--- a/src/com/android/server/telecom/CallEndpointController.java
+++ b/src/com/android/server/telecom/CallEndpointController.java
@@ -25,7 +25,9 @@
import android.telecom.CallEndpoint;
import android.telecom.CallEndpointException;
import android.telecom.Log;
+
import com.android.internal.annotations.VisibleForTesting;
+
import java.util.HashMap;
import java.util.Map;
import java.util.HashSet;
@@ -96,6 +98,12 @@
return;
}
+ if (isCurrentEndpointRequestedEndpoint(route, bluetoothAddress)) {
+ Log.d(this, "requestCallEndpointChange: requested endpoint is already active");
+ callback.send(CallEndpoint.ENDPOINT_OPERATION_SUCCESS, new Bundle());
+ return;
+ }
+
if (mPendingChangeRequest != null && !mPendingChangeRequest.isDone()) {
mPendingChangeRequest.complete(RESULT_ANOTHER_REQUEST);
mPendingChangeRequest = null;
@@ -116,6 +124,27 @@
mCallsManager.getCallAudioManager().setAudioRoute(route, bluetoothAddress);
}
+ public boolean isCurrentEndpointRequestedEndpoint(int requestedRoute, String requestedAddress) {
+ if (mCallsManager.getCallAudioManager() == null
+ || mCallsManager.getCallAudioManager().getCallAudioState() == null) {
+ return false;
+ }
+ CallAudioState currentAudioState = mCallsManager.getCallAudioManager().getCallAudioState();
+ // requested non-bt endpoint is already active
+ if (requestedRoute != CallAudioState.ROUTE_BLUETOOTH &&
+ requestedRoute == currentAudioState.getRoute()) {
+ return true;
+ }
+ // requested bt endpoint is already active
+ if (requestedRoute == CallAudioState.ROUTE_BLUETOOTH &&
+ currentAudioState.getActiveBluetoothDevice() != null &&
+ requestedAddress.equals(
+ currentAudioState.getActiveBluetoothDevice().getAddress())) {
+ return true;
+ }
+ return false;
+ }
+
private Bundle getErrorResult(int result) {
String message;
int resultCode;
@@ -165,8 +194,7 @@
for (Call call : calls) {
if (call != null && call.getConnectionService() != null) {
call.getConnectionService().onCallEndpointChanged(call, mActiveCallEndpoint);
- }
- else if (call != null && call.getTransactionServiceWrapper() != null) {
+ } else if (call != null && call.getTransactionServiceWrapper() != null) {
call.getTransactionServiceWrapper()
.onCallEndpointChanged(call, mActiveCallEndpoint);
}
@@ -181,8 +209,7 @@
if (call != null && call.getConnectionService() != null) {
call.getConnectionService().onAvailableCallEndpointsChanged(call,
mAvailableCallEndpoints);
- }
- else if (call != null && call.getTransactionServiceWrapper() != null) {
+ } else if (call != null && call.getTransactionServiceWrapper() != null) {
call.getTransactionServiceWrapper()
.onAvailableCallEndpointsChanged(call, mAvailableCallEndpoints);
}
@@ -196,8 +223,7 @@
for (Call call : calls) {
if (call != null && call.getConnectionService() != null) {
call.getConnectionService().onMuteStateChanged(call, isMuted);
- }
- else if (call != null && call.getTransactionServiceWrapper() != null) {
+ } else if (call != null && call.getTransactionServiceWrapper() != null) {
call.getTransactionServiceWrapper().onMuteStateChanged(call, isMuted);
}
}
@@ -207,7 +233,7 @@
Set<CallEndpoint> newAvailableEndpoints = new HashSet<>();
Map<ParcelUuid, String> newBluetoothDevices = new HashMap<>();
- mRouteToTypeMap.forEach((route, type)->{
+ mRouteToTypeMap.forEach((route, type) -> {
if ((state.getSupportedRouteMask() & route) != 0) {
if (type == CallEndpoint.TYPE_STREAMING) {
if (state.getRoute() == CallAudioState.ROUTE_STREAMING) {
diff --git a/src/com/android/server/telecom/CallStreamingController.java b/src/com/android/server/telecom/CallStreamingController.java
index 31b2235..6276a7d 100644
--- a/src/com/android/server/telecom/CallStreamingController.java
+++ b/src/com/android/server/telecom/CallStreamingController.java
@@ -36,7 +36,7 @@
import android.telecom.CallException;
import android.telecom.CallStreamingService;
import android.telecom.StreamingCall;
-import android.util.Log;
+import android.telecom.Log;
import com.android.internal.telecom.ICallStreamingService;
import com.android.server.telecom.voip.VoipCallTransaction;
@@ -65,6 +65,9 @@
private void onConnectedInternal(Call call, TransactionalServiceWrapper wrapper,
IBinder service) throws RemoteException {
synchronized (mLock) {
+ Log.i(this, "onConnectedInternal: callid=%s", call.getId());
+ Bundle extras = new Bundle();
+ extras.putString(StreamingCall.EXTRA_CALL_ID, call.getId());
mStreamingCall = call;
mTransactionalServiceWrapper = wrapper;
mService = ICallStreamingService.Stub.asInterface(service);
@@ -74,7 +77,7 @@
mService.onCallStreamingStarted(new StreamingCall(
mTransactionalServiceWrapper.getComponentName(),
mStreamingCall.getCallerDisplayName(),
- mStreamingCall.getContactUri(), new Bundle()));
+ mStreamingCall.getHandle(), extras));
mIsStreaming = true;
}
}
@@ -99,7 +102,6 @@
}
public static class QueryCallStreamingTransaction extends VoipCallTransaction {
- private static final String TAG = QueryCallStreamingTransaction.class.getSimpleName();
private final CallsManager mCallsManager;
public QueryCallStreamingTransaction(CallsManager callsManager) {
@@ -109,7 +111,7 @@
@Override
public CompletableFuture<VoipCallTransactionResult> processTransaction(Void v) {
- Log.d(TAG, "processTransaction");
+ Log.i(this, "processTransaction");
CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
if (mCallsManager.getCallStreamingController().isStreaming()) {
@@ -126,8 +128,6 @@
}
public static class AudioInterceptionTransaction extends VoipCallTransaction {
- private static final String TAG = AudioInterceptionTransaction.class.getSimpleName();
-
private Call mCall;
private boolean mEnterInterception;
@@ -140,7 +140,7 @@
@Override
public CompletableFuture<VoipCallTransactionResult> processTransaction(Void v) {
- Log.d(TAG, "processTransaction");
+ Log.d(this, "processTransaction");
CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
if (mEnterInterception) {
@@ -160,7 +160,6 @@
}
public class StreamingServiceTransaction extends VoipCallTransaction {
- private static final String TAG = "StreamingServiceTransaction";
public static final String MESSAGE = "STREAMING_FAILED_NO_SENDER";
private final TransactionalServiceWrapper mWrapper;
private final Context mContext;
@@ -179,13 +178,13 @@
@SuppressLint("LongLogTag")
@Override
public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
- Log.d(TAG, "processTransaction");
+ Log.d(this, "processTransaction");
CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
RoleManager roleManager = mContext.getSystemService(RoleManager.class);
PackageManager packageManager = mContext.getPackageManager();
if (roleManager == null || packageManager == null) {
- Log.e(TAG, "Can't find system service");
+ Log.w(this, "processTransaction: Can't find system service");
future.complete(new VoipCallTransactionResult(
VoipCallTransactionResult.RESULT_FAILED, MESSAGE));
return future;
@@ -194,7 +193,7 @@
List<String> holders = roleManager.getRoleHoldersAsUser(
RoleManager.ROLE_SYSTEM_CALL_STREAMING, mUserHandle);
if (holders.isEmpty()) {
- Log.e(TAG, "Can't find streaming app");
+ Log.w(this, "processTransaction: Can't find streaming app");
future.complete(new VoipCallTransactionResult(
VoipCallTransactionResult.RESULT_FAILED, MESSAGE));
return future;
@@ -205,7 +204,7 @@
List<ResolveInfo> infos = packageManager.queryIntentServicesAsUser(serviceIntent,
PackageManager.GET_META_DATA, mUserHandle);
if (infos.isEmpty()) {
- Log.e(TAG, "Can't find streaming service");
+ Log.w(this, "processTransaction: Can't find streaming service");
future.complete(new VoipCallTransactionResult(
VoipCallTransactionResult.RESULT_FAILED, MESSAGE));
return future;
@@ -215,7 +214,7 @@
if (serviceInfo.permission == null || !serviceInfo.permission.equals(
Manifest.permission.BIND_CALL_STREAMING_SERVICE)) {
- android.telecom.Log.w(TAG, "Must require BIND_CALL_STREAMING_SERVICE: " +
+ Log.w(this, "Must require BIND_CALL_STREAMING_SERVICE: " +
serviceInfo.packageName);
future.complete(new VoipCallTransactionResult(
VoipCallTransactionResult.RESULT_FAILED, MESSAGE));
@@ -228,7 +227,7 @@
if (!mContext.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE
| Context.BIND_FOREGROUND_SERVICE
| Context.BIND_SCHEDULE_LIKE_TOP_APP, mUserHandle)) {
- Log.e(TAG, "Can't bind to streaming service");
+ Log.w(this, "Can't bind to streaming service");
future.complete(new VoipCallTransactionResult(
VoipCallTransactionResult.RESULT_FAILED,
"STREAMING_FAILED_SENDER_BINDING_ERROR"));
@@ -243,8 +242,6 @@
}
public class UnbindStreamingServiceTransaction extends VoipCallTransaction {
- private static final String TAG = "UnbindStreamingServiceTransaction";
-
public UnbindStreamingServiceTransaction() {
super(mTelecomLock);
}
@@ -252,7 +249,7 @@
@SuppressLint("LongLogTag")
@Override
public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
- Log.d(TAG, "processTransaction");
+ Log.d(this, "processTransaction");
CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
resetController();
@@ -285,6 +282,7 @@
StreamingCall.STATE_HOLDING);
case CallState.DISCONNECTING:
case CallState.DISCONNECTED:
+ Log.addEvent(call, LogUtils.Events.STOP_STREAMING);
transaction = new CallStreamingStateChangeTransaction(
StreamingCall.STATE_DISCONNECTED);
default:
@@ -300,8 +298,8 @@
@Override
public void onError(CallException exception) {
- Log.e(String.valueOf(this), "Exception when set call "
- + "streaming state to streaming app: " + exception);
+ Log.e(this, exception, "Exception when set call "
+ + "streaming state to streaming app");
}
});
}
@@ -348,6 +346,7 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
+ Log.i(this, "onServiceConnected: " + name);
onConnectedInternal(mCall, mWrapper, service);
mFuture.complete(new VoipCallTransactionResult(
VoipCallTransactionResult.RESULT_SUCCEED, null));
@@ -380,7 +379,7 @@
mService.onCallStreamingStopped();
}
} catch (RemoteException e) {
- Log.w(String.valueOf(this), "Exception when stop call streaming:" + e);
+ Log.e(this, e, "Exception when stop call streaming");
}
resetController();
if (!mFuture.isDone()) {
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index e0e17cc..d457cc8 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -24,6 +24,7 @@
import static android.provider.CallLog.Calls.USER_MISSED_CALL_FILTERS_TIMEOUT;
import static android.provider.CallLog.Calls.USER_MISSED_CALL_SCREENING_SERVICE_SILENCED;
import static android.provider.CallLog.Calls.USER_MISSED_NEVER_RANG;
+import static android.provider.CallLog.Calls.USER_MISSED_NOT_RUNNING;
import static android.provider.CallLog.Calls.USER_MISSED_NO_ANSWER;
import static android.provider.CallLog.Calls.USER_MISSED_SHORT_RING;
import static android.telecom.TelecomManager.ACTION_POST_CALL;
@@ -446,6 +447,7 @@
private final CallStreamingController mCallStreamingController;
private final BlockedNumbersAdapter mBlockedNumbersAdapter;
private final TransactionManager mTransactionManager;
+ private final UserManager mUserManager;
private final ConnectionServiceFocusManager.CallsManagerRequester mRequester =
new ConnectionServiceFocusManager.CallsManagerRequester() {
@@ -685,6 +687,7 @@
mCallAnomalyWatchdog = callAnomalyWatchdog;
mAsyncTaskExecutor = asyncTaskExecutor;
+ mUserManager = mContext.getSystemService(UserManager.class);
}
public void setIncomingCallNotifier(IncomingCallNotifier incomingCallNotifier) {
@@ -1534,7 +1537,22 @@
CallFailureCause startFailCause =
checkIncomingCallPermitted(call, call.getTargetPhoneAccount());
- if (!isHandoverAllowed ||
+ // Check if the target phone account is possibly in ECBM.
+ call.setIsInECBM(getEmergencyCallHelper()
+ .isLastOutgoingEmergencyCallPAH(call.getTargetPhoneAccount()));
+ if (mUserManager.isQuietModeEnabled(call.getUserHandleFromTargetPhoneAccount())
+ && !call.isEmergencyCall() && !call.isInECBM()) {
+ Log.d(TAG, "Rejecting non-emergency call because the owner %s is not running.",
+ phoneAccountHandle.getUserHandle());
+ call.setMissedReason(USER_MISSED_NOT_RUNNING);
+ call.setStartFailCause(CallFailureCause.INVALID_USE);
+ if (isConference) {
+ notifyCreateConferenceFailed(phoneAccountHandle, call);
+ } else {
+ notifyCreateConnectionFailed(phoneAccountHandle, call);
+ }
+ }
+ else if (!isHandoverAllowed ||
(call.isSelfManaged() && !startFailCause.isSuccess())) {
if (isConference) {
notifyCreateConferenceFailed(phoneAccountHandle, call);
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index cc5932c..5b727ab 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -919,6 +919,9 @@
callingPhoneAccountHandle.getComponentName().getPackageName());
}
+ boolean hasCrossUserAccess = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS)
+ == PackageManager.PERMISSION_GRANTED;
long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
@@ -930,7 +933,7 @@
// an emergency call.
mPhoneAccountRegistrar.getCallCapablePhoneAccounts(null /*uriScheme*/,
false /*includeDisabledAccounts*/, userHandle, 0 /*capabilities*/,
- 0 /*excludedCapabilities*/, false);
+ 0 /*excludedCapabilities*/, hasCrossUserAccess);
PhoneAccountHandle phoneAccountHandle = null;
for (PhoneAccountHandle accountHandle : accountHandles) {
if(accountHandle.equals(callingPhoneAccountHandle)) {
diff --git a/src/com/android/server/telecom/EmergencyCallHelper.java b/src/com/android/server/telecom/EmergencyCallHelper.java
index a213e26..fbb666d 100644
--- a/src/com/android/server/telecom/EmergencyCallHelper.java
+++ b/src/com/android/server/telecom/EmergencyCallHelper.java
@@ -21,6 +21,8 @@
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.telecom.Log;
+import android.telecom.PhoneAccountHandle;
+
import com.android.internal.annotations.VisibleForTesting;
/**
@@ -34,6 +36,7 @@
private final DefaultDialerCache mDefaultDialerCache;
private final Timeouts.Adapter mTimeoutsAdapter;
private UserHandle mLocationPermissionGrantedToUser;
+ private PhoneAccountHandle mLastOutgoingEmergencyCallPAH;
//stores the original state of permissions that dialer had
private boolean mHadFineLocation = false;
@@ -46,6 +49,7 @@
private boolean mBackgroundLocationGranted = false;
private long mLastEmergencyCallTimestampMillis;
+ private long mLastOutgoingEmergencyCallTimestampMillis;
@VisibleForTesting
public EmergencyCallHelper(
@@ -63,7 +67,7 @@
grantLocationPermission(userHandle);
}
if (call != null && call.isEmergencyCall()) {
- recordEmergencyCallTime();
+ recordEmergencyCall(call);
}
}
@@ -78,15 +82,37 @@
return mLastEmergencyCallTimestampMillis;
}
- private void recordEmergencyCallTime() {
- mLastEmergencyCallTimestampMillis = System.currentTimeMillis();
+ void setLastOutgoingEmergencyCallPAH(PhoneAccountHandle accountHandle) {
+ mLastOutgoingEmergencyCallPAH = accountHandle;
}
- private boolean isInEmergencyCallbackWindow() {
- return System.currentTimeMillis() - getLastEmergencyCallTimeMillis()
+ public boolean isLastOutgoingEmergencyCallPAH(PhoneAccountHandle currentCallHandle) {
+ boolean ecbmActive = mLastOutgoingEmergencyCallPAH != null
+ && isInEmergencyCallbackWindow(mLastOutgoingEmergencyCallTimestampMillis)
+ && currentCallHandle != null
+ && currentCallHandle.equals(mLastOutgoingEmergencyCallPAH);
+ if (ecbmActive) {
+ Log.i(this, "ECBM is enabled for %s. The last recorded call timestamp was at %s",
+ currentCallHandle, mLastOutgoingEmergencyCallTimestampMillis);
+ }
+
+ return ecbmActive;
+ }
+
+ boolean isInEmergencyCallbackWindow(long lastEmergencyCallTimestampMillis) {
+ return System.currentTimeMillis() - lastEmergencyCallTimestampMillis
< mTimeoutsAdapter.getEmergencyCallbackWindowMillis(mContext.getContentResolver());
}
+ private void recordEmergencyCall(Call call) {
+ mLastEmergencyCallTimestampMillis = System.currentTimeMillis();
+ if (!call.isIncoming()) {
+ // ECBM is applicable to MO emergency calls
+ mLastOutgoingEmergencyCallTimestampMillis = mLastEmergencyCallTimestampMillis;
+ mLastOutgoingEmergencyCallPAH = call.getTargetPhoneAccount();
+ }
+ }
+
private boolean shouldGrantTemporaryLocationPermission(Call call) {
if (!mContext.getResources().getBoolean(R.bool.grant_location_permission_enabled)) {
Log.i(this, "ShouldGrantTemporaryLocationPermission, disabled by config");
@@ -96,7 +122,8 @@
Log.i(this, "ShouldGrantTemporaryLocationPermission, no call");
return false;
}
- if (!call.isEmergencyCall() && !isInEmergencyCallbackWindow()) {
+ if (!call.isEmergencyCall() && !isInEmergencyCallbackWindow(
+ getLastEmergencyCallTimeMillis())) {
Log.i(this, "ShouldGrantTemporaryLocationPermission, not emergency");
return false;
}
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 3215605..3d3e3b4 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -338,7 +338,10 @@
UserHandle userToBind = getUserFromCall(call);
boolean isManagedProfile = UserUtil.isManagedProfile(mContext, userToBind);
// 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.
+ // 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;
if (!mContext.bindServiceAsUser(intent, mServiceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
@@ -1191,6 +1194,11 @@
@Override
public void onCallAdded(Call call) {
UserHandle userFromCall = getUserFromCall(call);
+
+ Log.i(this, "onCallAdded: %s", call);
+ // Track the call if we don't already know about it.
+ addCall(call);
+
if (!isBoundAndConnectedToServices(userFromCall)) {
Log.i(this, "onCallAdded: %s; not bound or connected.", call);
// We are not bound, or we're not connected.
@@ -1206,10 +1214,6 @@
mEmergencyCallHelper.maybeGrantTemporaryLocationPermission(call,
userFromCall);
- Log.i(this, "onCallAdded: %s", call);
- // Track the call if we don't already know about it.
- addCall(call);
-
if (inCallServiceConnection != null) {
Log.i(this, "mInCallServiceConnection isConnected=%b",
inCallServiceConnection.isConnected());
@@ -2600,7 +2604,8 @@
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() && userManager.isQuietModeEnabled(userFromCall)) {
+ if ((call.isEmergencyCall() || call.isInECBM())
+ && userManager.isQuietModeEnabled(userFromCall)) {
return mCallsManager.getCurrentUserHandle();
}
return userFromCall;
diff --git a/src/com/android/server/telecom/LogUtils.java b/src/com/android/server/telecom/LogUtils.java
index 8ce5dc3..4e7546f 100644
--- a/src/com/android/server/telecom/LogUtils.java
+++ b/src/com/android/server/telecom/LogUtils.java
@@ -225,6 +225,8 @@
public static final String FLASH_NOTIFICATION_STOP = "FLASH_NOTIFICATION_STOP";
public static final String GAINED_FGS_DELEGATION = "GAINED_FGS_DELEGATION";
public static final String LOST_FGS_DELEGATION = "LOST_FGS_DELEGATION";
+ public static final String START_STREAMING = "START_STREAMING";
+ public static final String STOP_STREAMING = "STOP_STREAMING";
public static class Timings {
public static final String ACCEPT_TIMING = "accept";
diff --git a/src/com/android/server/telecom/TransactionalServiceWrapper.java b/src/com/android/server/telecom/TransactionalServiceWrapper.java
index 1e6403e..d83e551 100644
--- a/src/com/android/server/telecom/TransactionalServiceWrapper.java
+++ b/src/com/android/server/telecom/TransactionalServiceWrapper.java
@@ -425,6 +425,15 @@
@Override
public void onError(CallException exception) {
+ if (isAnswerRequest) {
+ // This also sends the signal to untrack from TSW and the client_TSW
+ removeCallFromCallsManager(call,
+ new DisconnectCause(DisconnectCause.REJECTED,
+ "client rejected to answer the call;"
+ + " force disconnecting"));
+ } else {
+ mCallsManager.markCallAsOnHold(call);
+ }
maybeResetForegroundCall(foregroundCallBeforeSwap, wasActive);
}
});
diff --git a/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java b/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
index 8b4ffed..93d9836 100644
--- a/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
+++ b/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
@@ -128,6 +128,8 @@
boolean success = latch.await(VoipCallTransaction.TIMEOUT_LIMIT, TimeUnit.MILLISECONDS);
if (!success) {
// client send onError and failed to complete transaction
+ Log.i(TAG, String.format("CallEventCallbackAckTransaction:"
+ + " client failed to complete the [%s] transaction", mAction));
return CompletableFuture.completedFuture(TRANSACTION_FAILED);
} else {
// success
diff --git a/src/com/android/server/telecom/voip/TransactionManager.java b/src/com/android/server/telecom/voip/TransactionManager.java
index 98faf3d..773dfb8 100644
--- a/src/com/android/server/telecom/voip/TransactionManager.java
+++ b/src/com/android/server/telecom/voip/TransactionManager.java
@@ -26,6 +26,8 @@
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Queue;
public class TransactionManager {
@@ -63,30 +65,31 @@
OutcomeReceiver<VoipCallTransactionResult, CallException> receiver) {
synchronized (sLock) {
mTransactions.add(transaction);
- transaction.setCompleteListener(new TransactionCompleteListener() {
- @Override
- public void onTransactionCompleted(VoipCallTransactionResult result,
- String transactionName){
- Log.i(TAG, String.format("transaction completed: with result=[%d]",
- result.getResult()));
- if (result.getResult() == TelecomManager.TELECOM_TRANSACTION_SUCCESS) {
- receiver.onResult(result);
- } else {
- receiver.onError(
- new CallException(result.getMessage(),
- result.getResult()));
- }
- finishTransaction();
- }
-
- @Override
- public void onTransactionTimeout(String transactionName){
- receiver.onError(new CallException(transactionName + " timeout",
- CODE_OPERATION_TIMED_OUT));
- finishTransaction();
- }
- });
}
+ transaction.setCompleteListener(new TransactionCompleteListener() {
+ @Override
+ public void onTransactionCompleted(VoipCallTransactionResult result,
+ String transactionName){
+ Log.i(TAG, String.format("transaction completed: with result=[%d]",
+ result.getResult()));
+ if (result.getResult() == TelecomManager.TELECOM_TRANSACTION_SUCCESS) {
+ receiver.onResult(result);
+ } else {
+ receiver.onError(
+ new CallException(result.getMessage(),
+ result.getResult()));
+ }
+ finishTransaction();
+ }
+
+ @Override
+ public void onTransactionTimeout(String transactionName){
+ receiver.onError(new CallException(transactionName + " timeout",
+ CODE_OPERATION_TIMED_OUT));
+ finishTransaction();
+ }
+ });
+
startTransactions();
}
@@ -102,8 +105,8 @@
return;
}
mCurrentTransaction = mTransactions.poll();
- mCurrentTransaction.start();
}
+ mCurrentTransaction.start();
}
private void finishTransaction() {
@@ -115,10 +118,12 @@
@VisibleForTesting
public void clear() {
+ List<VoipCallTransaction> pendingTransactions;
synchronized (sLock) {
- for (VoipCallTransaction transaction : mTransactions) {
- transaction.finish();
- }
+ pendingTransactions = new ArrayList<>(mTransactions);
+ }
+ for (VoipCallTransaction transaction : pendingTransactions) {
+ transaction.finish();
}
}
}
diff --git a/src/com/android/server/telecom/voip/VoipCallMonitor.java b/src/com/android/server/telecom/voip/VoipCallMonitor.java
index d0304a9..2a81051 100644
--- a/src/com/android/server/telecom/voip/VoipCallMonitor.java
+++ b/src/com/android/server/telecom/voip/VoipCallMonitor.java
@@ -207,8 +207,10 @@
synchronized (mLock) {
Log.i(this, "stopFGSDelegation of handle %s", handle);
Set<Call> calls = mPhoneAccountHandleListMap.get(handle);
- for (Call call : calls) {
- stopMonitorWorks(call);
+ if (calls != null) {
+ for (Call call : calls) {
+ stopMonitorWorks(call);
+ }
}
mPhoneAccountHandleListMap.remove(handle);
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
index 6670095..3ef8fbb 100644
--- a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
@@ -142,11 +142,6 @@
connection.setVideoState(request.getVideoState());
Log.i(this, "createSelfManagedConnection %s", connection);
mCallList.addConnection(connection);
- try {
- Thread.sleep(8000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
return connection;
}
}
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 4c9f52d..b868b70 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java
@@ -254,14 +254,14 @@
new OutcomeReceiver<Void, CallException>() {
@Override
public void onResult(Void result) {
- Log.i(TAG, String.format("success w/ %s", tag));
+ Log.i(TAG, String.format("requestEndpointChange: success w/ %s", tag));
updateCurrentEndpointWithOnResult(endpoint);
}
@Override
public void onError(CallException e) {
- Log.i(TAG, String.format("%s :failed to switch to endpoint=[%s],"
- + " due to exception=[%s]", tag, endpoint, e.toString()));
+ Log.i(TAG, String.format("requestEndpointChange: %s failed to switch to "
+ + "endpoint=[%s] due to exception=[%s]", tag, endpoint, e));
}
});
}
diff --git a/tests/src/com/android/server/telecom/tests/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index 453450d..9047da3 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -44,6 +44,7 @@
import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.BlockedNumberContract;
import android.telecom.Call;
import android.telecom.CallAudioState;
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
index 0f38ca5..d0a1d8b 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
@@ -131,7 +131,7 @@
assertEquals(CallAudioModeStateMachine.STREAMING_STATE_NAME, sm.getCurrentStateName());
verify(mAudioManager, never()).requestAudioFocusForCall(anyInt(), anyInt());
- verify(mAudioManager).setMode(eq(AudioManager.MODE_CALL_REDIRECT));
+ verify(mAudioManager).setMode(eq(AudioManager.MODE_COMMUNICATION_REDIRECT));
}
@SmallTest
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 413989b..22a850f 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -16,6 +16,8 @@
package com.android.server.telecom.tests;
+import static android.provider.CallLog.Calls.USER_MISSED_NOT_RUNNING;
+
import static junit.framework.Assert.assertNotNull;
import static junit.framework.TestCase.fail;
@@ -58,7 +60,9 @@
import android.os.ResultReceiver;
import android.os.SystemClock;
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;
@@ -2415,6 +2419,65 @@
assertEquals(DEFAULT_CALL_SCREENING_APP, outgoingCall.getPostCallPackageName());
}
+ @SmallTest
+ @Test
+ public void testRejectIncomingCallOnPAHInactive() throws Exception {
+ ConnectionServiceWrapper service = mock(ConnectionServiceWrapper.class);
+ doReturn(SIM_2_HANDLE.getComponentName()).when(service).getComponentName();
+ mCallsManager.addConnectionServiceRepositoryCache(SIM_2_HANDLE.getComponentName(),
+ SIM_2_HANDLE.getUserHandle(), service);
+
+ UserManager um = mContext.getSystemService(UserManager.class);
+ when(um.isQuietModeEnabled(eq(SIM_2_HANDLE.getUserHandle()))).thenReturn(true);
+ Call newCall = mCallsManager.processIncomingCallIntent(
+ SIM_2_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 testAcceptIncomingCallOnPAHInactiveAndECBMActive() throws Exception {
+ ConnectionServiceWrapper service = mock(ConnectionServiceWrapper.class);
+ doReturn(SIM_2_HANDLE.getComponentName()).when(service).getComponentName();
+ mCallsManager.addConnectionServiceRepositoryCache(SIM_2_HANDLE.getComponentName(),
+ SIM_2_HANDLE.getUserHandle(), service);
+
+ when(mEmergencyCallHelper.isLastOutgoingEmergencyCallPAH(eq(SIM_2_HANDLE)))
+ .thenReturn(true);
+ UserManager um = mContext.getSystemService(UserManager.class);
+ when(um.isQuietModeEnabled(eq(SIM_2_HANDLE.getUserHandle()))).thenReturn(true);
+ Call newCall = mCallsManager.processIncomingCallIntent(
+ SIM_2_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();
+ mCallsManager.addConnectionServiceRepositoryCache(SIM_2_HANDLE.getComponentName(),
+ SIM_2_HANDLE.getUserHandle(), service);
+
+ Bundle extras = new Bundle();
+ extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, TEST_ADDRESS);
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ UserManager um = mContext.getSystemService(UserManager.class);
+ when(um.isQuietModeEnabled(eq(SIM_2_HANDLE.getUserHandle()))).thenReturn(true);
+ when(tm.isEmergencyNumber(any(String.class))).thenReturn(true);
+ Call newCall = mCallsManager.processIncomingCallIntent(
+ SIM_2_HANDLE, extras, false);
+
+ assertFalse(newCall.isInECBM());
+ assertTrue(newCall.isEmergencyCall());
+ verify(service, timeout(TEST_TIMEOUT).times(0)).createConnectionFailed(any());
+ }
+
public class LatchedOutcomeReceiver implements OutcomeReceiver<Boolean,
CallException> {
CountDownLatch mCountDownLatch;
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index e61bc2a..16fd630 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -595,6 +595,34 @@
@MediumTest
@Test
public void
+ testBindToService_UserAssociatedWithCallIsInQuietMode_NonEmergCallECBM_BindsToPrimaryUser()
+ throws Exception {
+ when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockCall.isEmergencyCall()).thenReturn(false);
+ when(mMockCall.isInECBM()).thenReturn(true);
+ when(mMockCall.getUserHandleFromTargetPhoneAccount()).thenReturn(DUMMY_USER_HANDLE);
+ when(mMockContext.getSystemService(eq(UserManager.class)))
+ .thenReturn(mMockUserManager);
+ when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(true);
+ 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(mUserHandle));
+ 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);
@@ -1002,6 +1030,107 @@
}
/**
+ * Tests a case where InCallController DOES NOT bind to ANY InCallServices when the call is
+ * first added, but then one becomes available after the call starts. This test was originally
+ * added to reproduce a bug which would cause the call id mapper in the InCallController to not
+ * track a newly added call unless something was bound when the call was first added.
+ * @throws Exception
+ */
+ @MediumTest
+ @Test
+ public void testNoInitialBinding() throws Exception {
+ Bundle callExtras = new Bundle();
+ callExtras.putBoolean("whatever", true);
+
+ // Make a basic call
+ when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockCallsManager.isInEmergencyCall()).thenReturn(true);
+ when(mMockCall.isEmergencyCall()).thenReturn(true);
+ when(mMockContext.getSystemService(eq(UserManager.class)))
+ .thenReturn(mMockUserManager);
+ when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
+ when(mMockCall.isIncoming()).thenReturn(false);
+ when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
+ when(mMockCall.getIntentExtras()).thenReturn(callExtras);
+ when(mMockCall.isExternalCall()).thenReturn(false);
+ when(mMockCall.isSelfManaged()).thenReturn(true);
+ when(mMockCall.visibleToInCallService()).thenReturn(true);
+
+ // Dialer doesn't handle these calls, but non-UI ICS does.
+ when(mDefaultDialerCache.getDefaultDialerApplication(CURRENT_USER_ID))
+ .thenReturn(DEF_PKG);
+ ArgumentCaptor<ServiceConnection> serviceConnectionCaptor =
+ ArgumentCaptor.forClass(ServiceConnection.class);
+ when(mMockContext.bindServiceAsUser(any(Intent.class), serviceConnectionCaptor.capture(),
+ eq(serviceBindingFlags),
+ eq(mUserHandle))).thenReturn(true);
+ when(mTimeoutsAdapter.getEmergencyCallbackWindowMillis(any(ContentResolver.class)))
+ .thenReturn(300_000L);
+
+ // Setup package manager; there is a dialer and disable non-ui ICS
+ when(mMockPackageManager.queryIntentServicesAsUser(
+ any(Intent.class), anyInt(), anyInt())).thenReturn(
+ Arrays.asList(
+ getDefResolveInfo(false /* externalCalls */, false /* selfMgd */),
+ getNonUiResolveinfo(true /* selfManaged */,
+ false /* isEnabled */)
+ )
+ );
+ when(mMockPackageManager
+ .getComponentEnabledSetting(new ComponentName(DEF_PKG, DEF_CLASS)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+ when(mMockPackageManager
+ .getComponentEnabledSetting(new ComponentName(NONUI_PKG, NONUI_CLASS)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+
+ // Add the call.
+ mInCallController.onCallAdded(mMockCall);
+
+ // There will be 4 calls for the various types of ICS; this is normal.
+ verify(mMockPackageManager, times(4)).queryIntentServicesAsUser(
+ any(Intent.class),
+ eq(PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS),
+ eq(CURRENT_USER_ID));
+
+ // Verify no bind at this point
+ ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mMockContext, never()).bindServiceAsUser(
+ bindIntentCaptor.capture(),
+ any(ServiceConnection.class),
+ eq(serviceBindingFlags),
+ eq(mUserHandle));
+
+ // Setup mocks to enable non-ui ICS
+ when(mMockPackageManager.queryIntentServicesAsUser(
+ any(Intent.class), anyInt(), anyInt())).thenReturn(
+ Arrays.asList(
+ getDefResolveInfo(false /* externalCalls */, false /* selfMgd */),
+ getNonUiResolveinfo(true /* selfManaged */,
+ true /* isEnabled */)
+ )
+ );
+ when(mMockPackageManager
+ .getComponentEnabledSetting(new ComponentName(NONUI_PKG, NONUI_CLASS)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+
+ // Emulate a late enable of the non-ui ICS
+ Intent packageUpdated = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+ packageUpdated.setData(Uri.fromParts("package", NONUI_PKG, null));
+ packageUpdated.putExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST,
+ new String[] {NONUI_CLASS});
+ packageUpdated.putExtra(Intent.EXTRA_UID, NONUI_UID);
+ mRegisteredReceiver.onReceive(mMockContext, packageUpdated);
+
+ // Make sure we bound to it.
+ verify(mMockContext, times(1)).bindServiceAsUser(
+ bindIntentCaptor.capture(),
+ any(ServiceConnection.class),
+ eq(serviceBindingFlags),
+ eq(mUserHandle));
+ }
+
+ /**
* Ensures that the {@link InCallController} will bind to an {@link InCallService} which
* supports external calls.
*/