Merge "Override getDeviceId() of mock context"
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index cd50d24..64645a4 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -59,7 +59,7 @@
<string name="change_default_dialer_warning_message" msgid="8461963987376916114">"Deien aspektu guztiak erabili eta kontrolatu ahal izango ditu <xliff:g id="NEW_APP">%s</xliff:g> aplikazioak. Fidagarriak diren aplikazioak bakarrik erabili beharko lirateke Telefonoa aplikazio lehenetsi gisa."</string>
<string name="change_default_call_screening_dialog_title" msgid="5365787219927262408">"Deiak iragazteko aplikazio lehenetsia <xliff:g id="NEW_APP">%s</xliff:g> izatea nahi duzu?"</string>
<string name="change_default_call_screening_warning_message_for_disable_old_app" msgid="2039830033533243164">"Aurrerantzean, <xliff:g id="OLD_APP">%s</xliff:g> aplikazioak ezingo ditu iragazi deiak."</string>
- <string name="change_default_call_screening_warning_message" msgid="9020537562292754269">"Kontaktuetan gordeta ez dituzun deitzaileei buruzko informazioa ikusteko eta dei horiek blokeatzeko gai izango da <xliff:g id="NEW_APP">%s</xliff:g>. Aplikazio fidagarriak soilik ezarri beharko lirateke deiak iragazteko aplikazio lehenetsi gisa."</string>
+ <string name="change_default_call_screening_warning_message" msgid="9020537562292754269">"Kontaktuetan gordeta ez dauzkazun deitzaileei buruzko informazioa ikusteko eta dei horiek blokeatzeko gai izango da <xliff:g id="NEW_APP">%s</xliff:g>. Aplikazio fidagarriak soilik ezarri beharko lirateke deiak iragazteko aplikazio lehenetsi gisa."</string>
<string name="change_default_call_screening_dialog_affirmative" msgid="7162433828280058647">"Ezarri lehenetsi gisa"</string>
<string name="change_default_call_screening_dialog_negative" msgid="1839266125623106342">"Utzi"</string>
<string name="blocked_numbers" msgid="8322134197039865180">"Blokeatutako zenbakiak"</string>
diff --git a/src/com/android/server/telecom/CallAnomalyWatchdog.java b/src/com/android/server/telecom/CallAnomalyWatchdog.java
index b1c69e6..d3dbbc9 100644
--- a/src/com/android/server/telecom/CallAnomalyWatchdog.java
+++ b/src/com/android/server/telecom/CallAnomalyWatchdog.java
@@ -32,6 +32,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
@@ -110,6 +111,7 @@
private final TelecomSystem.SyncRoot mLock;
private final Timeouts.Adapter mTimeoutAdapter;
private final ClockProxy mClockProxy;
+ private AnomalyReporterAdapter mAnomalyReporter = new AnomalyReporterAdapterImpl();
// Pre-allocate space for 2 calls; realistically thats all we should ever need (tm)
private final Map<Call, ScheduledFuture<?>> mScheduledFutureMap = new ConcurrentHashMap<>(2);
private final Map<Call, WatchdogCallState> mWatchdogCallStateMap = new ConcurrentHashMap<>(2);
@@ -125,6 +127,22 @@
*/
private static final String ENABLE_DISCONNECT_CALL_ON_STUCK_STATE =
"enable_disconnect_call_on_stuck_state";
+ /**
+ * Anomaly Report UUIDs and corresponding event descriptions specific to CallAnomalyWatchdog.
+ */
+ public static final UUID WATCHDOG_DISCONNECTED_STUCK_CALL_UUID =
+ UUID.fromString("4b093985-c78f-45e3-a9fe-5319f397b025");
+ public static final String WATCHDOG_DISCONNECTED_STUCK_CALL_MSG =
+ "Telecom CallAnomalyWatchdog caught and disconnected a stuck/zombie call.";
+ public static final UUID WATCHDOG_DISCONNECTED_STUCK_EMERGENCY_CALL_UUID =
+ UUID.fromString("d57d8aab-d723-485e-a0dd-d1abb0f346c8");
+ public static final String WATCHDOG_DISCONNECTED_STUCK_EMERGENCY_CALL_MSG =
+ "Telecom CallAnomalyWatchdog caught and disconnected a stuck/zombie emergency call.";
+
+ @VisibleForTesting
+ public void setAnomalyReporterAdapter(AnomalyReporterAdapter mAnomalyReporterAdapter){
+ mAnomalyReporter = mAnomalyReporterAdapter;
+ }
public CallAnomalyWatchdog(ScheduledExecutorService executorService,
TelecomSystem.SyncRoot lock,
@@ -306,6 +324,15 @@
Log.addEvent(call, STATE_TIMEOUT, newState);
mLocalLog.log("STATE_TIMEOUT; callId=" + call.getId() + " in state "
+ newState);
+ if (call.isEmergencyCall()){
+ mAnomalyReporter.reportAnomaly(
+ WATCHDOG_DISCONNECTED_STUCK_EMERGENCY_CALL_UUID,
+ WATCHDOG_DISCONNECTED_STUCK_EMERGENCY_CALL_MSG);
+ } else {
+ mAnomalyReporter.reportAnomaly(
+ WATCHDOG_DISCONNECTED_STUCK_CALL_UUID,
+ WATCHDOG_DISCONNECTED_STUCK_CALL_MSG);
+ }
if (isEnabledDisconnect) {
call.setOverrideDisconnectCauseCode(
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 9960a44..8cac314 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -486,19 +486,20 @@
CallAudioRouteStateMachine.INCLUDE_BLUETOOTH_IN_BASELINE);
}
- Set<UserHandle> silenceRingers(Context context, UserHandle callingUser) {
+ Set<UserHandle> silenceRingers(Context context, UserHandle callingUser,
+ boolean hasCrossUserPermission) {
// Store all users from calls that were silenced so that we can silence the
// InCallServices which are associated with those users.
Set<UserHandle> userHandles = new HashSet<>();
boolean allCallSilenced = true;
synchronized (mCallsManager.getLock()) {
for (Call call : mRingingCalls) {
- PhoneAccount targetPhoneAccount = call.getPhoneAccountFromHandle();
UserHandle userFromCall = call.getUserHandleFromTargetPhoneAccount();
// Do not try to silence calls when calling user is different from the phone account
- // user and the account does not have CAPABILITY_MULTI_USER enabled.
- if (!callingUser.equals(userFromCall) && !targetPhoneAccount.
- hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
+ // user, the account does not have CAPABILITY_MULTI_USER enabled, or if the user
+ // does not have the INTERACT_ACROSS_USERS permission enabled.
+ if (!hasCrossUserPermission && !mCallsManager
+ .isCallVisibleForUser(call, callingUser)) {
allCallSilenced = false;
continue;
}
diff --git a/src/com/android/server/telecom/CallStreamingController.java b/src/com/android/server/telecom/CallStreamingController.java
index 912d47a..56ce17b 100644
--- a/src/com/android/server/telecom/CallStreamingController.java
+++ b/src/com/android/server/telecom/CallStreamingController.java
@@ -18,6 +18,7 @@
import static android.telecom.CallStreamingService.STREAMING_FAILED_SENDER_BINDING_ERROR;
+import android.Manifest;
import android.annotation.SuppressLint;
import android.app.role.RoleManager;
import android.content.ComponentName;
@@ -206,6 +207,15 @@
}
ServiceInfo serviceInfo = infos.get(0).serviceInfo;
+
+ if (serviceInfo.permission == null || !serviceInfo.permission.equals(
+ Manifest.permission.BIND_CALL_STREAMING_SERVICE)) {
+ android.telecom.Log.w(TAG, "Must require BIND_CALL_STREAMING_SERVICE: " +
+ serviceInfo.packageName);
+ future.complete(new VoipCallTransactionResult(
+ VoipCallTransactionResult.RESULT_FAILED, MESSAGE));
+ return future;
+ }
Intent intent = new Intent(CallStreamingService.SERVICE_INTERFACE);
intent.setComponent(serviceInfo.getComponentName());
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index ed83880..48aa9e6 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -61,6 +61,7 @@
import android.media.ToneGenerator;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -107,6 +108,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import android.widget.Button;
import com.android.internal.annotations.VisibleForTesting;
@@ -611,7 +613,8 @@
mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer,
ringtoneFactory, systemVibrator,
new Ringer.VibrationEffectProxy(), mInCallController,
- mContext.getSystemService(NotificationManager.class));
+ mContext.getSystemService(NotificationManager.class),
+ mContext.getSystemService(AccessibilityManager.class));
mCallRecordingTonePlayer = new CallRecordingTonePlayer(mContext, audioManager,
mTimeoutsAdapter, mLock);
mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
@@ -3676,10 +3679,6 @@
return false;
}
- boolean hasActiveOrHoldingCall() {
- return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null;
- }
-
boolean hasRingingCall() {
return getFirstCallWithState(CallState.RINGING, CallState.ANSWERED) != null;
}
@@ -3801,15 +3800,6 @@
return getFirstCallWithState(CallState.ACTIVE);
}
- Call getDialingCall() {
- return getFirstCallWithState(CallState.DIALING);
- }
-
- @VisibleForTesting
- public Call getHeldCall() {
- return getFirstCallWithState(CallState.ON_HOLD);
- }
-
public Call getHeldCallByConnectionService(PhoneAccountHandle targetPhoneAccount) {
Optional<Call> heldCall = mCalls.stream()
.filter(call -> PhoneAccountHandle.areFromSamePackage(call.getTargetPhoneAccount(),
@@ -4422,6 +4412,60 @@
return (int) callsStream.count();
}
+ /**
+ * Determines the number of calls (visible to the calling user) matching the specified criteria.
+ * This is an overloaded method which is being used in a security patch to fix up the call
+ * state type APIs which are acting across users when they should not be.
+ *
+ * See {@link TelecomManager#isInCall()} and {@link TelecomManager#isInManagedCall()}.
+ *
+ * @param callFilter indicates whether to include just managed calls
+ * ({@link #CALL_FILTER_MANAGED}), self-managed calls
+ * ({@link #CALL_FILTER_SELF_MANAGED}), or all calls
+ * ({@link #CALL_FILTER_ALL}).
+ * @param excludeCall Where {@code non-null}, this call is excluded from the count.
+ * @param callingUser Where {@code non-null}, call visibility is scoped to this
+ * {@link UserHandle}.
+ * @param hasCrossUserAccess indicates if calling user has the INTERACT_ACROSS_USERS permission.
+ * @param phoneAccountHandle Where {@code non-null}, calls for this {@link PhoneAccountHandle}
+ * are excluded from the count.
+ * @param states The list of {@link CallState}s to include in the count.
+ * @return Count of calls matching criteria.
+ */
+ @VisibleForTesting
+ public int getNumCallsWithState(final int callFilter, Call excludeCall,
+ UserHandle callingUser, boolean hasCrossUserAccess,
+ PhoneAccountHandle phoneAccountHandle, int... states) {
+
+ Set<Integer> desiredStates = IntStream.of(states).boxed().collect(Collectors.toSet());
+
+ Stream<Call> callsStream = mCalls.stream()
+ .filter(call -> desiredStates.contains(call.getState()) &&
+ call.getParentCall() == null && !call.isExternalCall());
+
+ if (callFilter == CALL_FILTER_MANAGED) {
+ callsStream = callsStream.filter(call -> !call.isSelfManaged());
+ } else if (callFilter == CALL_FILTER_SELF_MANAGED) {
+ callsStream = callsStream.filter(call -> call.isSelfManaged());
+ }
+
+ // If a call to exclude was specified, filter it out.
+ if (excludeCall != null) {
+ callsStream = callsStream.filter(call -> call != excludeCall);
+ }
+
+ // If a phone account handle was specified, only consider calls for that phone account.
+ if (phoneAccountHandle != null) {
+ callsStream = callsStream.filter(
+ call -> phoneAccountHandle.equals(call.getTargetPhoneAccount()));
+ }
+
+ callsStream = callsStream.filter(
+ call -> hasCrossUserAccess || isCallVisibleForUser(call, callingUser));
+
+ return (int) callsStream.count();
+ }
+
private boolean hasMaximumLiveCalls(Call exceptCall) {
return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(CALL_FILTER_ALL,
exceptCall, null /* phoneAccountHandle*/, LIVE_CALL_STATES);
@@ -4516,23 +4560,29 @@
/**
* Determines if there are any ongoing managed or self-managed calls.
* Note: The {@link #ONGOING_CALL_STATES} are
+ * @param callingUser The user to scope the calls to.
+ * @param hasCrossUserAccess indicates if user has the INTERACT_ACROSS_USERS permission.
* @return {@code true} if there are ongoing managed or self-managed calls, {@code false}
* otherwise.
*/
- public boolean hasOngoingCalls() {
+ public boolean hasOngoingCalls(UserHandle callingUser, boolean hasCrossUserAccess) {
return getNumCallsWithState(
CALL_FILTER_ALL, null /* excludeCall */,
+ callingUser, hasCrossUserAccess,
null /* phoneAccountHandle */,
ONGOING_CALL_STATES) > 0;
}
/**
* Determines if there are any ongoing managed calls.
+ * @param callingUser The user to scope the calls to.
+ * @param hasCrossUserAccess indicates if user has the INTERACT_ACROSS_USERS permission.
* @return {@code true} if there are ongoing managed calls, {@code false} otherwise.
*/
- public boolean hasOngoingManagedCalls() {
+ public boolean hasOngoingManagedCalls(UserHandle callingUser, boolean hasCrossUserAccess) {
return getNumCallsWithState(
CALL_FILTER_MANAGED, null /* excludeCall */,
+ callingUser, hasCrossUserAccess,
null /* phoneAccountHandle */,
ONGOING_CALL_STATES) > 0;
}
@@ -6013,6 +6063,19 @@
}
/**
+ * Determines if a {@link Call} is visible to the calling user. If the {@link PhoneAccount} has
+ * CAPABILITY_MULTI_USER, or the user handle associated with the {@link PhoneAccount} is the
+ * same as the calling user, the call is visible to the user.
+ * @param call
+ * @return {@code true} if call is visible to the calling user
+ */
+ boolean isCallVisibleForUser(Call call, UserHandle userHandle) {
+ return call.getUserHandleFromTargetPhoneAccount().equals(userHandle)
+ || call.getPhoneAccountFromHandle()
+ .hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER);
+ }
+
+ /**
* Determines if two {@link Call} instances originated from either the same target
* {@link PhoneAccountHandle} or connection manager {@link PhoneAccountHandle}.
* @param call1 The first call
diff --git a/src/com/android/server/telecom/LogUtils.java b/src/com/android/server/telecom/LogUtils.java
index 0481de1..35f8d1e 100644
--- a/src/com/android/server/telecom/LogUtils.java
+++ b/src/com/android/server/telecom/LogUtils.java
@@ -214,6 +214,8 @@
public static final String SET_VOIP_MODE = "SET_VOIP_MODE";
public static final String STATE_TIMEOUT = "STATE_TIMEOUT";
public static final String ICS_EXTRAS_CHANGED = "ICS_EXTRAS_CHANGED";
+ public static final String FLASH_NOTIFICATION_START = "FLASH_NOTIFICATION_START";
+ public static final String FLASH_NOTIFICATION_STOP = "FLASH_NOTIFICATION_STOP";
public static class Timings {
public static final String ACCEPT_TIMING = "accept";
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 2828d12..171745f 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -37,6 +37,7 @@
import android.os.Vibrator;
import android.telecom.Log;
import android.telecom.TelecomManager;
+import android.view.accessibility.AccessibilityManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.telecom.LogUtils.EventTimer;
@@ -161,6 +162,7 @@
private RingtoneFactory mRingtoneFactory;
private AudioManager mAudioManager;
private NotificationManager mNotificationManager;
+ private AccessibilityManager mAccessibilityManager;
/**
* Call objects that are ringing, vibrating or call-waiting. These are used only for logging
@@ -194,7 +196,8 @@
Vibrator vibrator,
VibrationEffectProxy vibrationEffectProxy,
InCallController inCallController,
- NotificationManager notificationManager) {
+ NotificationManager notificationManager,
+ AccessibilityManager accessibilityManager) {
mLock = new Object();
mSystemSettingsUtil = systemSettingsUtil;
@@ -208,6 +211,7 @@
mInCallController = inCallController;
mVibrationEffectProxy = vibrationEffectProxy;
mNotificationManager = notificationManager;
+ mAccessibilityManager = accessibilityManager;
if (mContext.getResources().getBoolean(R.bool.use_simple_vibration_pattern)) {
mDefaultVibrationEffect = mVibrationEffectProxy.createWaveform(SIMPLE_VIBRATION_PATTERN,
@@ -261,7 +265,7 @@
attributes = ringerAttributesFuture.get(
RINGER_ATTRIBUTES_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException e) {
- // Keep attributs as null
+ // Keep attributes as null
Log.i(this, "getAttributes error: " + e);
}
@@ -290,6 +294,13 @@
stopCallWaiting();
+ final boolean shouldFlash = attributes.shouldRingForContact();
+ if (mAccessibilityManager != null && shouldFlash) {
+ Log.addEvent(foregroundCall, LogUtils.Events.FLASH_NOTIFICATION_START);
+ getHandler().post(() -> mAccessibilityManager.startFlashNotificationSequence(mContext,
+ AccessibilityManager.FLASH_REASON_CALL));
+ }
+
// Determine if the settings and DND mode indicate that the vibrator can be used right now.
final boolean isVibratorEnabled =
isVibratorEnabled(mContext, attributes.shouldRingForContact());
@@ -427,6 +438,7 @@
"hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b",
mVibrator.hasVibrator(), mSystemSettingsUtil.isRingVibrationEnabled(mContext),
mAudioManager.getRingerMode(), mIsVibrating);
+ mVibratingCall = foregroundCall;
mIsVibrating = true;
mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
Log.i(this, "start vibration.");
@@ -499,6 +511,12 @@
}
public void stopRinging() {
+ final Call foregroundCall = mRingingCall != null ? mRingingCall : mVibratingCall;
+ if (mAccessibilityManager != null) {
+ Log.addEvent(foregroundCall, LogUtils.Events.FLASH_NOTIFICATION_STOP);
+ getHandler().post(() -> mAccessibilityManager.stopFlashNotificationSequence(mContext));
+ }
+
synchronized (mLock) {
if (mRingingCall != null) {
Log.addEvent(mRingingCall, LogUtils.Events.STOP_RINGER);
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 57987de..6826290 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -362,15 +362,12 @@
}
synchronized (mLock) {
final UserHandle callingUserHandle = Binder.getCallingUserHandle();
- boolean hasCrossUserAccess = mContext.checkCallingOrSelfPermission(
- Manifest.permission.INTERACT_ACROSS_USERS)
- == PackageManager.PERMISSION_GRANTED;
long token = Binder.clearCallingIdentity();
try {
return new ParceledListSlice<>(
mPhoneAccountRegistrar.getCallCapablePhoneAccounts(null,
includeDisabledAccounts, callingUserHandle,
- hasCrossUserAccess));
+ hasInAppCrossUserPermission()));
} catch (Exception e) {
Log.e(this, e, "getCallCapablePhoneAccounts");
mAnomalyReporter.reportAnomaly(GET_CALL_CAPABLE_ACCOUNTS_ERROR_UUID,
@@ -640,13 +637,11 @@
synchronized (mLock) {
final UserHandle callingUserHandle = Binder.getCallingUserHandle();
- boolean hasCrossUserAccess = mContext.checkCallingOrSelfPermission(
- Manifest.permission.INTERACT_ACROSS_USERS)
- == PackageManager.PERMISSION_GRANTED;
long token = Binder.clearCallingIdentity();
try {
return new ParceledListSlice<>(mPhoneAccountRegistrar
- .getAllPhoneAccountHandles(callingUserHandle, hasCrossUserAccess));
+ .getAllPhoneAccountHandles(callingUserHandle,
+ hasInAppCrossUserPermission()));
} catch (Exception e) {
Log.e(this, e, "getAllPhoneAccounts");
throw e;
@@ -954,7 +949,8 @@
try {
Log.i(this, "Silence Ringer requested by %s", callingPackage);
Set<UserHandle> userHandles = mCallsManager.getCallAudioManager().
- silenceRingers(mContext, callingUserHandle);
+ silenceRingers(mContext, callingUserHandle,
+ hasInAppCrossUserPermission());
mCallsManager.getInCallController().silenceRinger(userHandles);
} finally {
Binder.restoreCallingIdentity(token);
@@ -1071,7 +1067,8 @@
}
synchronized (mLock) {
- return mCallsManager.hasOngoingCalls();
+ return mCallsManager.hasOngoingCalls(Binder.getCallingUserHandle(),
+ hasInAppCrossUserPermission());
}
} finally {
Log.endSession();
@@ -1112,7 +1109,8 @@
}
synchronized (mLock) {
- return mCallsManager.hasOngoingManagedCalls();
+ return mCallsManager.hasOngoingManagedCalls(Binder.getCallingUserHandle(),
+ hasInAppCrossUserPermission());
}
} finally {
Log.endSession();
@@ -2802,6 +2800,12 @@
+ " INTERACT_ACROSS_USERS permission");
}
+ private boolean hasInAppCrossUserPermission() {
+ return mContext.checkCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
// to be used for TestApi methods that can only be called with SHELL UID.
private void enforceShellOnly(int callingUid, String message) {
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index 2b5a80b..473e7b9 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -650,7 +650,12 @@
BluetoothDevice activeDevice = mBluetoothRouteManager.getBluetoothAudioConnectedDevice();
Log.i(this, "isInbandRingingEnabled: activeDevice: " + activeDevice);
if (mBluetoothRouteManager.isCachedLeAudioDevice(activeDevice)) {
- return true;
+ if (mBluetoothLeAudioService == null) {
+ Log.i(this, "isInbandRingingEnabled: no leaudio service available.");
+ return false;
+ }
+ int groupId = mBluetoothLeAudioService.getGroupId(activeDevice);
+ return mBluetoothLeAudioService.isInbandRingtoneEnabled(groupId);
} else {
if (mBluetoothHeadset == null) {
Log.i(this, "isInbandRingingEnabled: no headset service available.");
diff --git a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
index 95493e9..cdf2efa 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
@@ -16,8 +16,7 @@
package com.android.server.telecom.transactionalVoipApp;
-
-import android.telecom.CallAudioState;
+import android.telecom.CallControlCallback;
import android.telecom.CallEndpoint;
import android.telecom.CallControl;
import android.telecom.CallEventCallback;
@@ -28,7 +27,7 @@
import java.util.function.Consumer;
-public class MyVoipCall implements CallEventCallback {
+public class MyVoipCall implements CallControlCallback, CallEventCallback {
private static final String TAG = "MyVoipCall";
private final String mCallId;
@@ -99,6 +98,5 @@
@Override
public void onMuteStateChanged(boolean isMuted) {
-
}
}
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 775947b..4e1ec4c 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/VoipAppMainActivity.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/VoipAppMainActivity.java
@@ -135,7 +135,7 @@
}
},
- call);
+ call, call);
}
private void setCallActive(MyVoipCall call) {
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
index 799f989..c37d136 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
@@ -522,6 +522,8 @@
@Test
public void testInBandRingingEnabledForLeDevice() {
when(mBluetoothHeadset.isInbandRingingEnabled()).thenReturn(false);
+ when(mBluetoothLeAudio.isInbandRingtoneEnabled(1)).thenReturn(true);
+ when(mBluetoothLeAudio.getGroupId(eq(device3))).thenReturn(1);
receiverUnderTest.onReceive(mContext,
buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1,
BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
diff --git a/tests/src/com/android/server/telecom/tests/CallAnomalyWatchdogTest.java b/tests/src/com/android/server/telecom/tests/CallAnomalyWatchdogTest.java
index efbebf1..93d1314 100644
--- a/tests/src/com/android/server/telecom/tests/CallAnomalyWatchdogTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAnomalyWatchdogTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
@@ -28,6 +29,7 @@
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
+import com.android.server.telecom.AnomalyReporterAdapter;
import com.android.server.telecom.Call;
import com.android.server.telecom.CallAnomalyWatchdog;
import com.android.server.telecom.CallState;
@@ -81,6 +83,7 @@
@Mock private ToastFactory mMockToastProxy;
@Mock private PhoneNumberUtilsAdapter mMockPhoneNumberUtilsAdapter;
@Mock private ConnectionServiceWrapper mMockConnectionService;
+ @Mock private AnomalyReporterAdapter mAnomalyReporterAdapter;
@Override
@Before
@@ -114,6 +117,7 @@
.when(mMockConnectionService).getComponentName();
mCallAnomalyWatchdog = new CallAnomalyWatchdog(mTestScheduledExecutorService, mLock,
mTimeouts, mMockClockProxy);
+ mCallAnomalyWatchdog.setAnomalyReporterAdapter(mAnomalyReporterAdapter);
}
@Override
@@ -123,6 +127,21 @@
}
/**
+ * Helper function that setups the call being tested.
+ */
+ private Call setupCallHelper(int callState, boolean isCreateConnectionComplete,
+ ConnectionServiceWrapper service, boolean isVoipAudioMode, boolean isEmergencyCall) {
+ Call call = getCall();
+ call.setState(callState, "foo");
+ call.setIsCreateConnectionComplete(isCreateConnectionComplete);
+ if (service != null) call.setConnectionService(service);
+ call.setIsVoipAudioMode(isVoipAudioMode);
+ call.setIsEmergencyCall(isEmergencyCall);
+ mCallAnomalyWatchdog.onCallAdded(call);
+ return call;
+ }
+
+ /**
* Test that the anomaly call state class correctly reports whether the state is transitory or
* not for the purposes of the call anomaly watchdog.
*/
@@ -269,12 +288,7 @@
*/
@Test
public void testAddVoipRingingCall() {
- Call call = getCall();
- call.setState(CallState.RINGING, "foo");
- call.setIsCreateConnectionComplete(false);
- call.setIsVoipAudioMode(true);
- call.setIsEmergencyCall(false);
- mCallAnomalyWatchdog.onCallCreated(call);
+ Call call = setupCallHelper(CallState.RINGING, false, null, true, false);
// Newly created call which hasn't been added; should schedule timeout.
assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -308,12 +322,7 @@
*/
@Test
public void testAddVoipEmergencyRingingCall() {
- Call call = getCall();
- call.setState(CallState.RINGING, "foo");
- call.setIsCreateConnectionComplete(false);
- call.setIsVoipAudioMode(true);
- call.setIsEmergencyCall(true);
- mCallAnomalyWatchdog.onCallCreated(call);
+ Call call = setupCallHelper(CallState.RINGING, false, null, true, true);
// Newly created call which hasn't been added; should schedule timeout.
assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -348,12 +357,7 @@
*/
@Test
public void testAddNonVoipRingingCall() {
- Call call = getCall();
- call.setState(CallState.RINGING, "foo");
- call.setIsCreateConnectionComplete(false);
- call.setIsVoipAudioMode(false);
- call.setIsEmergencyCall(false);
- mCallAnomalyWatchdog.onCallCreated(call);
+ Call call = setupCallHelper(CallState.RINGING, false, null, false, false);
// Newly created call which hasn't been added; should schedule timeout.
assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -388,12 +392,7 @@
*/
@Test
public void testAddNonVoipEmergencyRingingCall() {
- Call call = getCall();
- call.setState(CallState.RINGING, "foo");
- call.setIsCreateConnectionComplete(false);
- call.setIsVoipAudioMode(false);
- call.setIsEmergencyCall(true);
- mCallAnomalyWatchdog.onCallCreated(call);
+ Call call = setupCallHelper(CallState.RINGING, false, null, false, true);
// Newly created call which hasn't been added; should schedule timeout.
assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -426,12 +425,7 @@
*/
@Test
public void testAddVoipRingingCallTimeoutWithoutConnection() {
- Call call = getCall();
- call.setState(CallState.RINGING, "foo");
- call.setIsCreateConnectionComplete(false);
- call.setIsVoipAudioMode(true);
- call.setIsEmergencyCall(false);
- mCallAnomalyWatchdog.onCallCreated(call);
+ setupCallHelper(CallState.RINGING, false, null, true, false);
// Newly created call which hasn't been added; should schedule timeout.
assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -454,12 +448,7 @@
*/
@Test
public void testAddVoipEmergencyRingingCallTimeoutWithoutConnection() {
- Call call = getCall();
- call.setState(CallState.RINGING, "foo");
- call.setIsCreateConnectionComplete(false);
- call.setIsVoipAudioMode(true);
- call.setIsEmergencyCall(true);
- mCallAnomalyWatchdog.onCallCreated(call);
+ setupCallHelper(CallState.RINGING, false, null, true, true);
// Newly created call which hasn't been added; should schedule timeout.
assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -483,12 +472,7 @@
*/
@Test
public void testAddNonVoipRingingCallTimeoutWithoutConnection() {
- Call call = getCall();
- call.setState(CallState.RINGING, "foo");
- call.setIsCreateConnectionComplete(false);
- call.setIsVoipAudioMode(false);
- call.setIsEmergencyCall(false);
- mCallAnomalyWatchdog.onCallCreated(call);
+ setupCallHelper(CallState.RINGING, false, null, false, false);;
// Newly created call which hasn't been added; should schedule timeout.
assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -511,12 +495,7 @@
*/
@Test
public void testAddNonVoipEmergencyRingingCallTimeoutWithoutConnection() {
- Call call = getCall();
- call.setState(CallState.RINGING, "foo");
- call.setIsCreateConnectionComplete(false);
- call.setIsVoipAudioMode(false);
- call.setIsEmergencyCall(true);
- mCallAnomalyWatchdog.onCallCreated(call);
+ setupCallHelper(CallState.RINGING, false, null, false, true);
// Newly created call which hasn't been added; should schedule timeout.
assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -540,13 +519,7 @@
*/
@Test
public void testAddVoipRingingCallTimeoutWithConnection() {
- Call call = getCall();
- call.setState(CallState.RINGING, "foo");
- call.setIsCreateConnectionComplete(true);
- call.setConnectionService(mMockConnectionService);
- call.setIsVoipAudioMode(true);
- call.setIsEmergencyCall(false);
- mCallAnomalyWatchdog.onCallAdded(call);
+ setupCallHelper(CallState.RINGING, true, mMockConnectionService, true, false);
// Newly created call which hasn't been added; should schedule timeout.
assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -569,13 +542,7 @@
*/
@Test
public void testAddVoipEmergencyRingingCallTimeoutWithConnection() {
- Call call = getCall();
- call.setState(CallState.RINGING, "foo");
- call.setIsCreateConnectionComplete(true);
- call.setConnectionService(mMockConnectionService);
- call.setIsVoipAudioMode(true);
- call.setIsEmergencyCall(true);
- mCallAnomalyWatchdog.onCallAdded(call);
+ setupCallHelper(CallState.RINGING, true, mMockConnectionService, true, true);
// Newly created call which hasn't been added; should schedule timeout.
assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -599,13 +566,7 @@
*/
@Test
public void testAddNonVoipRingingCallTimeoutWithConnection() {
- Call call = getCall();
- call.setState(CallState.RINGING, "foo");
- call.setIsCreateConnectionComplete(true);
- call.setConnectionService(mMockConnectionService);
- call.setIsVoipAudioMode(false);
- call.setIsEmergencyCall(false);
- mCallAnomalyWatchdog.onCallAdded(call);
+ setupCallHelper(CallState.RINGING, true, mMockConnectionService, false, false);
// Newly created call which hasn't been added; should schedule timeout.
assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -628,13 +589,7 @@
*/
@Test
public void testAddNonVoipEmergencyRingingCallTimeoutWithConnection() {
- Call call = getCall();
- call.setState(CallState.RINGING, "foo");
- call.setIsCreateConnectionComplete(true);
- call.setConnectionService(mMockConnectionService);
- call.setIsVoipAudioMode(false);
- call.setIsEmergencyCall(true);
- mCallAnomalyWatchdog.onCallAdded(call);
+ setupCallHelper(CallState.RINGING, true, mMockConnectionService, false, true);
// Newly created call which hasn't been added; should schedule timeout.
assertEquals(1, mTestScheduledExecutorService.getNumberOfScheduledRunnables());
@@ -659,12 +614,7 @@
@Test
public void testVoipPlaceCallTimeout() {
// Call will start in connecting state
- Call call = getCall();
- call.setState(CallState.CONNECTING, "foo");
- call.setIsCreateConnectionComplete(false);
- call.setIsVoipAudioMode(true);
- call.setIsEmergencyCall(false);
- mCallAnomalyWatchdog.onCallCreated(call);
+ Call call = setupCallHelper(CallState.CONNECTING, false, null, true, false);
// Assume it is created but the app never sets it to a proper state
call.setIsCreateConnectionComplete(false);
@@ -682,18 +632,61 @@
}
/**
+ * Emulate the case where a new outgoing VoIP call is added to the watchdog.
+ * In this case, the timeout will fire in transitory state and should report an anomaly.
+ */
+ @Test
+ public void testVoipPlaceCallTimeoutReportAnomaly() {
+ // Call will start in connecting state
+ Call call = setupCallHelper(CallState.CONNECTING, false, null, true, false);
+
+ // Assume it is created but the app never sets it to a proper state
+ call.setIsCreateConnectionComplete(false);
+ mCallAnomalyWatchdog.onCallAdded(call);
+
+ // Move the clock to fire the timeout.
+ when(mMockClockProxy.elapsedRealtime()).thenReturn(TEST_VOIP_TRANSITORY_MILLIS + 1);
+ mTestScheduledExecutorService.advanceTime(TEST_VOIP_TRANSITORY_MILLIS + 1);
+
+ //Ensure an anomaly was reported
+ verify(mAnomalyReporterAdapter).reportAnomaly(
+ CallAnomalyWatchdog.WATCHDOG_DISCONNECTED_STUCK_CALL_UUID,
+ CallAnomalyWatchdog.WATCHDOG_DISCONNECTED_STUCK_CALL_MSG);
+ }
+
+ /**
+ * Emulate the case where a new outgoing VoIP emergency call is added to the watchdog.
+ * In this case, the timeout will fire in transitory state and should report an emergency
+ * anomaly.
+ */
+ @Test
+ public void testVoipEmergencyPlaceCallTimeoutReportAnomaly() {
+ // Call will start in connecting state
+ Call call = setupCallHelper(CallState.CONNECTING, false, null, true, true);
+
+ // Assume it is created but the app never sets it to a proper state
+ call.setIsCreateConnectionComplete(false);
+ mCallAnomalyWatchdog.onCallAdded(call);
+
+ // Move the clock to fire the timeout.
+ when(mMockClockProxy.elapsedRealtime()).
+ thenReturn(TEST_VOIP_EMERGENCY_TRANSITORY_MILLIS + 1);
+ mTestScheduledExecutorService.advanceTime(TEST_VOIP_EMERGENCY_TRANSITORY_MILLIS + 1);
+
+ //Ensure an anomaly was reported
+ verify(mAnomalyReporterAdapter).reportAnomaly(
+ CallAnomalyWatchdog.WATCHDOG_DISCONNECTED_STUCK_EMERGENCY_CALL_UUID,
+ CallAnomalyWatchdog.WATCHDOG_DISCONNECTED_STUCK_EMERGENCY_CALL_MSG);
+ }
+
+ /**
* Emulate the case where a new outgoing VoIP emergency call is added to the watchdog.
* In this case, the timeout will fire in transitory state.
*/
@Test
public void testVoipEmergencyPlaceCallTimeout() {
// Call will start in connecting state
- Call call = getCall();
- call.setState(CallState.CONNECTING, "foo");
- call.setIsCreateConnectionComplete(false);
- call.setIsVoipAudioMode(true);
- call.setIsEmergencyCall(true);
- mCallAnomalyWatchdog.onCallCreated(call);
+ Call call = setupCallHelper(CallState.CONNECTING, false, null, true, true);
// Assume it is created but the app never sets it to a proper state
call.setIsCreateConnectionComplete(false);
@@ -718,12 +711,7 @@
@Test
public void testNonVoipPlaceCallTimeout() {
// Call will start in connecting state
- Call call = getCall();
- call.setState(CallState.CONNECTING, "foo");
- call.setIsCreateConnectionComplete(false);
- call.setIsVoipAudioMode(false);
- call.setIsEmergencyCall(false);
- mCallAnomalyWatchdog.onCallCreated(call);
+ Call call = setupCallHelper(CallState.CONNECTING, false, null, false, false);
// Assume it is created but the app never sets it to a proper state
call.setIsCreateConnectionComplete(false);
@@ -747,12 +735,7 @@
@Test
public void testNonVoipEmergencyPlaceCallTimeout() {
// Call will start in connecting state
- Call call = getCall();
- call.setState(CallState.CONNECTING, "foo");
- call.setIsCreateConnectionComplete(false);
- call.setIsVoipAudioMode(false);
- call.setIsEmergencyCall(true);
- mCallAnomalyWatchdog.onCallCreated(call);
+ Call call = setupCallHelper(CallState.CONNECTING, false, null, false, true);
// Assume it is created but the app never sets it to a proper state
call.setIsCreateConnectionComplete(false);
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index f754611..75a9a96 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -42,10 +42,12 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.Manifest;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -162,6 +164,13 @@
| PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
.setIsEnabled(true)
.build();
+ private static final PhoneAccount SIM_1_ACCOUNT_SECONDARY = new PhoneAccount
+ .Builder(SIM_1_HANDLE_SECONDARY, "Sim1")
+ .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
+ | PhoneAccount.CAPABILITY_CALL_PROVIDER
+ | PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
+ .setIsEnabled(true)
+ .build();
private static final PhoneAccount SIM_2_ACCOUNT = new PhoneAccount.Builder(SIM_2_HANDLE, "Sim2")
.setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
| PhoneAccount.CAPABILITY_CALL_PROVIDER
@@ -2228,6 +2237,29 @@
waitForCountDownLatch(latch);
}
+ @SmallTest
+ @Test
+ public void testGetNumCallsWithState_MultiUser() throws Exception {
+ when(mContext.checkCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ // Add call under secondary user
+ Call call = addSpyCall(SIM_1_HANDLE_SECONDARY, CallState.ACTIVE);
+ when(call.getPhoneAccountFromHandle()).thenReturn(SIM_1_ACCOUNT_SECONDARY);
+ // Verify that call is visible to primary user
+ assertEquals(mCallsManager.getNumCallsWithState(0, null,
+ UserHandle.CURRENT_OR_SELF, true,
+ null, CallState.ACTIVE), 1);
+ // Verify that call is not visible to primary user
+ // when a different phone account handle is specified.
+ assertEquals(mCallsManager.getNumCallsWithState(0, null,
+ UserHandle.CURRENT_OR_SELF, true,
+ SIM_1_HANDLE, CallState.ACTIVE), 0);
+ // Deny INTERACT_ACROSS_USERS permission and verify that call is not visible to primary user
+ assertEquals(mCallsManager.getNumCallsWithState(0, null,
+ UserHandle.CURRENT_OR_SELF, false,
+ null, CallState.ACTIVE), 0);
+ }
+
public void waitForCountDownLatch(CountDownLatch latch) throws InterruptedException {
boolean success = latch.await(5000, TimeUnit.MILLISECONDS);
if (!success) {
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index d71365c..0743805 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -75,6 +75,7 @@
import android.telephony.TelephonyRegistryManager;
import android.test.mock.MockContext;
import android.util.DisplayMetrics;
+import android.view.accessibility.AccessibilityManager;
import java.io.File;
import java.io.IOException;
@@ -237,6 +238,8 @@
return mPermissionCheckerManager;
case Context.SENSOR_PRIVACY_SERVICE:
return mSensorPrivacyManager;
+ case Context.ACCESSIBILITY_SERVICE:
+ return mAccessibilityManager;
default:
return null;
}
@@ -270,6 +273,8 @@
return Context.SENSOR_PRIVACY_SERVICE;
} else if (svcClass == NotificationManager.class) {
return Context.NOTIFICATION_SERVICE;
+ } else if (svcClass == AccessibilityManager.class) {
+ return Context.ACCESSIBILITY_SERVICE;
}
throw new UnsupportedOperationException();
}
@@ -567,6 +572,7 @@
private final LocationManager mLocationManager = mock(LocationManager.class);
private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class);
private final NotificationManager mNotificationManager = mock(NotificationManager.class);
+ private final AccessibilityManager mAccessibilityManager = mock(AccessibilityManager.class);
private final UserManager mUserManager = mock(UserManager.class);
private final StatusBarManager mStatusBarManager = mock(StatusBarManager.class);
private SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
index 8407bc9..35eda43 100644
--- a/tests/src/com/android/server/telecom/tests/RingerTest.java
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -50,6 +50,7 @@
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.test.suitebuilder.annotation.SmallTest;
+import android.view.accessibility.AccessibilityManager;
import com.android.server.telecom.AsyncRingtonePlayer;
import com.android.server.telecom.Call;
@@ -149,12 +150,13 @@
@Mock Vibrator mockVibrator;
@Mock InCallController mockInCallController;
@Mock NotificationManager mockNotificationManager;
+ @Mock AccessibilityManager mockAccessibilityManager;
+
@Spy Ringer.VibrationEffectProxy spyVibrationEffectProxy;
@Mock InCallTonePlayer mockTonePlayer;
@Mock Call mockCall1;
@Mock Call mockCall2;
- @Mock NotificationManager mNotificationManager;
private static final PhoneAccountHandle PA_HANDLE =
new PhoneAccountHandle(new ComponentName("pa_pkg", "pa_cls"),
@@ -174,26 +176,22 @@
return new UriVibrationEffect(ringtoneUriForEffect);
}).when(spyVibrationEffectProxy).get(any(), any());
when(mockPlayerFactory.createPlayer(anyInt())).thenReturn(mockTonePlayer);
- mockAudioManager =
- (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- mNotificationManager = mContext.getSystemService(NotificationManager.class);
-
+ mockAudioManager = mContext.getSystemService(AudioManager.class);
when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mockSystemSettingsUtil.isHapticPlaybackSupported(any(Context.class))).thenReturn(true);
- mockNotificationManager =
- (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mockNotificationManager =mContext.getSystemService(NotificationManager.class);
+ mockAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
when(mockTonePlayer.startTone()).thenReturn(true);
when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
when(mockRingtoneFactory.hasHapticChannels(any(Ringtone.class))).thenReturn(false);
mRingerUnderTest = new Ringer(mockPlayerFactory, mContext, mockSystemSettingsUtil,
mockRingtonePlayer, mockRingtoneFactory, mockVibrator, spyVibrationEffectProxy,
- mockInCallController, mNotificationManager);
+ mockInCallController, mockNotificationManager, mockAccessibilityManager);
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());
mRingerUnderTest.setBlockOnRingingFuture(mRingCompletionFuture);
- mRingerUnderTest.setNotificationManager(mockNotificationManager);
}
@Override
@@ -522,8 +520,9 @@
when(mockCall1.wasDndCheckComputedForCall()).thenReturn(false);
when(mockCall1.getHandle()).thenReturn(Uri.parse(""));
- when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
- when(mNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(false);
+ when(mContext.getSystemService(NotificationManager.class)).thenReturn(
+ mockNotificationManager);
+ when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(false);
// THEN
assertFalse(mRingerUnderTest.shouldRingForContact(mockCall1));
@@ -541,13 +540,60 @@
// WHEN
when(mockCall1.wasDndCheckComputedForCall()).thenReturn(false);
when(mockCall1.getHandle()).thenReturn(Uri.parse(""));
- when(mNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
+ when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
// THEN
assertTrue(mRingerUnderTest.shouldRingForContact(mockCall1));
verify(mockCall1, atLeastOnce()).setCallIsSuppressedByDoNotDisturb(false);
}
+ @Test
+ public void testNoFlashNotificationWhenCallSuppressed() {
+ ensureRingtoneMocked();
+ // Start call waiting to make sure that it doesn't stop when we start ringing
+ mRingerUnderTest.startCallWaiting(mockCall1);
+ when(mockCall2.wasDndCheckComputedForCall()).thenReturn(false);
+ when(mockCall2.getHandle()).thenReturn(Uri.parse(""));
+ when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(false);
+
+ assertFalse(mRingerUnderTest.shouldRingForContact(mockCall2));
+ assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
+ verify(mockAccessibilityManager, never())
+ .startFlashNotificationSequence(any(Context.class), anyInt());
+ }
+
+ @Test
+ public void testStartFlashNotificationWhenRingStarts() {
+ ensureRingtoneMocked();
+ // Start call waiting to make sure that it doesn't stop when we start ringing
+ mRingerUnderTest.startCallWaiting(mockCall1);
+ when(mockCall2.wasDndCheckComputedForCall()).thenReturn(false);
+ when(mockCall2.getHandle()).thenReturn(Uri.parse(""));
+ when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
+
+ assertTrue(mRingerUnderTest.shouldRingForContact(mockCall2));
+ assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
+ verify(mockAccessibilityManager, atLeastOnce())
+ .startFlashNotificationSequence(any(Context.class), anyInt());
+ }
+
+ @Test
+ public void testStopFlashNotificationWhenRingStops() {
+ ensureRingtoneMocked();
+ // Start call waiting to make sure that it doesn't stop when we start ringing
+ mRingerUnderTest.startCallWaiting(mockCall1);
+ when(mockCall2.wasDndCheckComputedForCall()).thenReturn(false);
+ when(mockCall2.getHandle()).thenReturn(Uri.parse(""));
+ when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
+
+ assertTrue(mRingerUnderTest.shouldRingForContact(mockCall2));
+ assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
+ mRingerUnderTest.stopRinging();
+ verify(mockAccessibilityManager, atLeastOnce())
+ .stopFlashNotificationSequence(any(Context.class));
+
+ }
+
private void ensureRingerIsAudible() {
when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(100);
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index a95d14e..569d6b4 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -24,6 +24,7 @@
import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+import android.Manifest;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.ComponentName;
@@ -219,6 +220,8 @@
doReturn(mContext).when(mContext).createContextAsUser(any(UserHandle.class), anyInt());
doNothing().when(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class),
anyString());
+ when(mContext.checkCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
doAnswer(invocation -> {
mDefaultDialerObserver = invocation.getArgument(1);
return null;
@@ -1463,8 +1466,7 @@
public void testEndCallWithNoForegroundCall() throws Exception {
Call call = mock(Call.class);
when(call.getState()).thenReturn(CallState.ACTIVE);
- when(mFakeCallsManager.getFirstCallWithState(any()))
- .thenReturn(call);
+ when(mFakeCallsManager.getFirstCallWithState(any())).thenReturn(call);
assertTrue(mTSIBinder.endCall(TEST_PACKAGE));
verify(mFakeCallsManager).disconnectCall(eq(call));
}
@@ -1505,14 +1507,16 @@
@SmallTest
@Test
public void testIsInCall() throws Exception {
- when(mFakeCallsManager.hasOngoingCalls()).thenReturn(true);
+ when(mFakeCallsManager.hasOngoingCalls(any(UserHandle.class), anyBoolean()))
+ .thenReturn(true);
assertTrue(mTSIBinder.isInCall(DEFAULT_DIALER_PACKAGE, null));
}
@SmallTest
@Test
public void testNotIsInCall() throws Exception {
- when(mFakeCallsManager.hasOngoingCalls()).thenReturn(false);
+ when(mFakeCallsManager.hasOngoingCalls(any(UserHandle.class), anyBoolean()))
+ .thenReturn(false);
assertFalse(mTSIBinder.isInCall(DEFAULT_DIALER_PACKAGE, null));
}
@@ -1527,20 +1531,22 @@
} catch (SecurityException e) {
// desired result
}
- verify(mFakeCallsManager, never()).hasOngoingCalls();
+ verify(mFakeCallsManager, never()).hasOngoingCalls(any(UserHandle.class), anyBoolean());
}
@SmallTest
@Test
public void testIsInManagedCall() throws Exception {
- when(mFakeCallsManager.hasOngoingManagedCalls()).thenReturn(true);
+ when(mFakeCallsManager.hasOngoingManagedCalls(any(UserHandle.class), anyBoolean()))
+ .thenReturn(true);
assertTrue(mTSIBinder.isInManagedCall(DEFAULT_DIALER_PACKAGE, null));
}
@SmallTest
@Test
public void testNotIsInManagedCall() throws Exception {
- when(mFakeCallsManager.hasOngoingManagedCalls()).thenReturn(false);
+ when(mFakeCallsManager.hasOngoingManagedCalls(any(UserHandle.class), anyBoolean()))
+ .thenReturn(false);
assertFalse(mTSIBinder.isInManagedCall(DEFAULT_DIALER_PACKAGE, null));
}
@@ -1555,7 +1561,7 @@
} catch (SecurityException e) {
// desired result
}
- verify(mFakeCallsManager, never()).hasOngoingCalls();
+ verify(mFakeCallsManager, never()).hasOngoingCalls(any(UserHandle.class), anyBoolean());
}
/**