Merge "Support notion of ECBM for work profiles." into udc-dev am: 9cc2f6d839 am: fa2cc6e8d6
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/Telecomm/+/22934856
Change-Id: Ibbca620470247c65845984f177a452afb9db9c7a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 743156e..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);
}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index ccc8e59..b9f8b40 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/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 be27dd1..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
@@ -2601,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/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/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index fc4e935..98fedc3 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;
@@ -2357,6 +2361,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 7d7a829..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);