Merge "Add new default vibration for notifications" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-06-01 18:21:08 +00:00
committed by Android (Google) Code Review
10 changed files with 367 additions and 100 deletions

View File

@@ -212,9 +212,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.notification.Adjustment;
@@ -370,12 +368,8 @@ public class NotificationManagerService extends SystemService {
// 1 second past the ANR timeout.
static final int FINISH_TOKEN_TIMEOUT = 11 * 1000;
static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
static final long SNOOZE_UNTIL_UNSPECIFIED = -1;
static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
static final int INVALID_UID = -1;
static final String ROOT_PKG = "root";
@@ -483,7 +477,6 @@ public class NotificationManagerService extends SystemService {
AudioManagerInternal mAudioManagerInternal;
// Can be null for wear
@Nullable StatusBarManagerInternal mStatusBar;
Vibrator mVibrator;
private WindowManagerInternal mWindowManagerInternal;
private AlarmManager mAlarmManager;
private ICompanionDeviceManager mCompanionManager;
@@ -505,7 +498,6 @@ public class NotificationManagerService extends SystemService {
private LogicalLight mNotificationLight;
LogicalLight mAttentionLight;
private long[] mFallbackVibrationPattern;
private boolean mUseAttentionLight;
boolean mHasLight = true;
boolean mLightEnabled;
@@ -584,6 +576,7 @@ public class NotificationManagerService extends SystemService {
RankingHelper mRankingHelper;
@VisibleForTesting
PreferencesHelper mPreferencesHelper;
private VibratorHelper mVibratorHelper;
private final UserProfiles mUserProfiles = new UserProfiles();
private NotificationListeners mListeners;
@@ -1598,10 +1591,7 @@ public class NotificationManagerService extends SystemService {
mVibrateNotificationKey = null;
final long identity = Binder.clearCallingIdentity();
try {
// Stop all vibrations with usage of class alarm (ringtone, alarm, notification usages).
int usageFilter =
VibrationAttributes.USAGE_CLASS_ALARM | ~VibrationAttributes.USAGE_CLASS_MASK;
mVibrator.cancel(usageFilter);
mVibratorHelper.cancelVibration();
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1995,19 +1985,6 @@ public class NotificationManagerService extends SystemService {
private SettingsObserver mSettingsObserver;
protected ZenModeHelper mZenModeHelper;
static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
int[] ar = r.getIntArray(resid);
if (ar == null) {
return def;
}
final int len = ar.length > maxlen ? maxlen : ar.length;
long[] out = new long[len];
for (int i=0; i<len; i++) {
out[i] = ar[i];
}
return out;
}
public NotificationManagerService(Context context) {
this(context,
new NotificationRecordLoggerImpl(),
@@ -2041,13 +2018,18 @@ public class NotificationManagerService extends SystemService {
}
@VisibleForTesting
void setHints(int hints) {
mListenerHints = hints;
VibratorHelper getVibratorHelper() {
return mVibratorHelper;
}
@VisibleForTesting
void setVibrator(Vibrator vibrator) {
mVibrator = vibrator;
void setVibratorHelper(VibratorHelper helper) {
mVibratorHelper = helper;
}
@VisibleForTesting
void setHints(int hints) {
mListenerHints = hints;
}
@VisibleForTesting
@@ -2121,11 +2103,6 @@ public class NotificationManagerService extends SystemService {
mHandler = handler;
}
@VisibleForTesting
void setFallbackVibrationPattern(long[] vibrationPattern) {
mFallbackVibrationPattern = vibrationPattern;
}
@VisibleForTesting
void setPackageManager(IPackageManager packageManager) {
mPackageManager = packageManager;
@@ -2200,7 +2177,6 @@ public class NotificationManagerService extends SystemService {
mPackageManager = packageManager;
mPackageManagerClient = packageManagerClient;
mAppOps = appOps;
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
mAppUsageStats = appUsageStats;
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mCompanionManager = companionManager;
@@ -2288,6 +2264,7 @@ public class NotificationManagerService extends SystemService {
extractorNames);
mSnoozeHelper = snoozeHelper;
mGroupHelper = groupHelper;
mVibratorHelper = new VibratorHelper(getContext());
mHistoryManager = historyManager;
// This is a ManagedServices object that keeps track of the listeners.
@@ -2309,10 +2286,6 @@ public class NotificationManagerService extends SystemService {
mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
mFallbackVibrationPattern = getLongArray(resources,
R.array.config_notificationFallbackVibePattern,
VIBRATE_PATTERN_MAXLEN,
DEFAULT_VIBRATE_PATTERN);
mInCallNotificationUri = Uri.parse("file://" +
resources.getString(R.string.config_inCallNotificationSound));
mInCallNotificationAudioAttributes = new AudioAttributes.Builder()
@@ -7439,7 +7412,7 @@ public class NotificationManagerService extends SystemService {
if (mSystemReady && mAudioManager != null) {
Uri soundUri = record.getSound();
hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
long[] vibration = record.getVibration();
VibrationEffect vibration = record.getVibration();
// Demote sound to vibration if vibration missing & phone in vibration mode.
if (vibration == null
&& hasValidSound
@@ -7447,7 +7420,8 @@ public class NotificationManagerService extends SystemService {
== AudioManager.RINGER_MODE_VIBRATE)
&& mAudioManager.getStreamVolume(
AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) == 0) {
vibration = mFallbackVibrationPattern;
boolean insistent = (record.getFlags() & Notification.FLAG_INSISTENT) != 0;
vibration = mVibratorHelper.createFallbackVibration(insistent);
}
hasValidVibrate = vibration != null;
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
@@ -7674,23 +7648,12 @@ public class NotificationManagerService extends SystemService {
return false;
}
private boolean playVibration(final NotificationRecord record, long[] vibration,
private boolean playVibration(final NotificationRecord record, final VibrationEffect effect,
boolean delayVibForSound) {
// Escalate privileges so we can use the vibrator even if the
// notifying app does not have the VIBRATE permission.
final long identity = Binder.clearCallingIdentity();
try {
final VibrationEffect effect;
try {
final boolean insistent =
(record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
effect = VibrationEffect.createWaveform(
vibration, insistent ? 0 : -1 /*repeatIndex*/);
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Error creating vibration waveform with pattern: " +
Arrays.toString(vibration));
return false;
}
if (delayVibForSound) {
new Thread(() -> {
// delay the vibration by the same amount as the notification sound
@@ -7727,8 +7690,7 @@ public class NotificationManagerService extends SystemService {
// to the reason so we can still debug from bugreports
String reason = "Notification (" + record.getSbn().getOpPkg() + " "
+ record.getSbn().getUid() + ") " + (delayed ? "(Delayed)" : "");
mVibrator.vibrate(Process.SYSTEM_UID, PackageManagerService.PLATFORM_PACKAGE_NAME,
effect, reason, record.getAudioAttributes());
mVibratorHelper.vibrate(effect, record.getAudioAttributes(), reason);
}
private boolean isNotificationForCurrentUser(NotificationRecord record) {

View File

@@ -47,6 +47,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.provider.Settings;
import android.service.notification.Adjustment;
import android.service.notification.NotificationListenerService;
@@ -158,7 +159,7 @@ public final class NotificationRecord {
private String mUserExplanation;
private boolean mPreChannelsNotification = true;
private Uri mSound;
private long[] mVibration;
private VibrationEffect mVibration;
private AudioAttributes mAttributes;
private NotificationChannel mChannel;
private ArrayList<String> mPeopleOverride;
@@ -287,29 +288,28 @@ public final class NotificationRecord {
return light;
}
private long[] calculateVibration() {
long[] vibration;
final long[] defaultVibration = NotificationManagerService.getLongArray(
mContext.getResources(),
com.android.internal.R.array.config_defaultNotificationVibePattern,
NotificationManagerService.VIBRATE_PATTERN_MAXLEN,
NotificationManagerService.DEFAULT_VIBRATE_PATTERN);
private VibrationEffect calculateVibration() {
VibratorHelper helper = new VibratorHelper(mContext);
final Notification notification = getSbn().getNotification();
final boolean insistent = (notification.flags & Notification.FLAG_INSISTENT) != 0;
VibrationEffect defaultVibration = helper.createDefaultVibration(insistent);
VibrationEffect vibration;
if (getChannel().shouldVibrate()) {
vibration = getChannel().getVibrationPattern() == null
? defaultVibration : getChannel().getVibrationPattern();
? defaultVibration
: helper.createWaveformVibration(getChannel().getVibrationPattern(), insistent);
} else {
vibration = null;
}
if (mPreChannelsNotification
&& (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
final Notification notification = getSbn().getNotification();
final boolean useDefaultVibrate =
(notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
if (useDefaultVibrate) {
vibration = defaultVibration;
} else {
vibration = notification.vibrate;
vibration = helper.createWaveformVibration(notification.vibrate, insistent);
}
}
return vibration;
@@ -1071,7 +1071,7 @@ public final class NotificationRecord {
return mSound;
}
public long[] getVibration() {
public VibrationEffect getVibration() {
return mVibration;
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.notification;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.media.AudioAttributes;
import android.os.Process;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Slog;
import com.android.internal.R;
import com.android.server.pm.PackageManagerService;
import java.util.Arrays;
/**
* NotificationManagerService helper for functionality related to the vibrator.
*/
public final class VibratorHelper {
private static final String TAG = "NotificationVibratorHelper";
private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
private final Vibrator mVibrator;
private final long[] mDefaultPattern;
private final long[] mFallbackPattern;
public VibratorHelper(Context context) {
mVibrator = context.getSystemService(Vibrator.class);
mDefaultPattern = getLongArray(
context.getResources(),
com.android.internal.R.array.config_defaultNotificationVibePattern,
VIBRATE_PATTERN_MAXLEN,
DEFAULT_VIBRATE_PATTERN);
mFallbackPattern = getLongArray(context.getResources(),
R.array.config_notificationFallbackVibePattern,
VIBRATE_PATTERN_MAXLEN,
DEFAULT_VIBRATE_PATTERN);
}
/**
* Safely create a {@link VibrationEffect} from given vibration {@code pattern}.
*
* <p>This method returns {@code null} if the pattern is also {@code null} or invalid.
*
* @param pattern The off/on vibration pattern, where each item is a duration in milliseconds.
* @param insistent {@code true} if the vibration should loop until it is cancelled.
*/
@Nullable
public static VibrationEffect createWaveformVibration(@Nullable long[] pattern,
boolean insistent) {
try {
if (pattern != null) {
return VibrationEffect.createWaveform(pattern, /* repeat= */ insistent ? 0 : -1);
}
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Error creating vibration waveform with pattern: "
+ Arrays.toString(pattern));
}
return null;
}
/**
* Vibrate the device with given {@code effect}.
*
* <p>We need to vibrate as "android" so we can breakthrough DND.
*/
public void vibrate(VibrationEffect effect, AudioAttributes attrs, String reason) {
mVibrator.vibrate(Process.SYSTEM_UID, PackageManagerService.PLATFORM_PACKAGE_NAME,
effect, reason, attrs);
}
/** Stop all notification vibrations (ringtone, alarm, notification usages). */
public void cancelVibration() {
int usageFilter =
VibrationAttributes.USAGE_CLASS_ALARM | ~VibrationAttributes.USAGE_CLASS_MASK;
mVibrator.cancel(usageFilter);
}
/**
* Creates a vibration to be used as fallback when the device is in vibrate mode.
*
* @param insistent {@code true} if the vibration should loop until it is cancelled.
*/
public VibrationEffect createFallbackVibration(boolean insistent) {
if (mVibrator.hasFrequencyControl()) {
return createChirpVibration(insistent);
}
return createWaveformVibration(mFallbackPattern, insistent);
}
/**
* Creates a vibration to be used by notifications without a custom pattern.
*
* @param insistent {@code true} if the vibration should loop until it is cancelled.
*/
public VibrationEffect createDefaultVibration(boolean insistent) {
if (mVibrator.hasFrequencyControl()) {
return createChirpVibration(insistent);
}
return createWaveformVibration(mDefaultPattern, insistent);
}
private static VibrationEffect createChirpVibration(boolean insistent) {
VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform()
.addStep(/* amplitude= */ 0, /* frequency= */ -0.85f, /* duration= */ 0)
.addRamp(/* amplitude= */ 1, /* frequency= */ -0.25f, /* duration= */ 100)
.addStep(/* amplitude= */ 1, /* duration= */ 150)
.addRamp(/* amplitude= */ 0, /* frequency= */ -0.85f, /* duration= */ 250);
if (insistent) {
return waveformBuilder.build(/* repeat= */ 0);
}
VibrationEffect singleBeat = waveformBuilder.build();
return VibrationEffect.startComposition()
.addEffect(singleBeat)
.addEffect(singleBeat)
.compose();
}
private static long[] getLongArray(Resources resources, int resId, int maxLength, long[] def) {
int[] ar = resources.getIntArray(resId);
if (ar == null) {
return def;
}
final int len = ar.length > maxLength ? maxLength : ar.length;
long[] out = new long[len];
for (int i = 0; i < len; i++) {
out[i] = ar[i];
}
return out;
}
}

View File

@@ -67,7 +67,7 @@ final class VibratorController {
mNativeWrapper = nativeWrapper;
mNativeWrapper.init(vibratorId, listener);
// TODO(b/167947076): load suggested range from config
mVibratorInfo = mNativeWrapper.getInfo(/* suggestedFrequencyRange= */ 100);
mVibratorInfo = mNativeWrapper.getInfo(/* suggestedFrequencyRange= */ 200);
Preconditions.checkNotNull(mVibratorInfo, "Failed to retrieve data for vibrator %d",
vibratorId);
}

View File

@@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -297,7 +298,7 @@ public class VibratorControllerTest {
private void mockVibratorCapabilities(int capabilities) {
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
when(mNativeWrapperMock.getInfo(/* suggestedFrequencyRange= */ 100)).thenReturn(
when(mNativeWrapperMock.getInfo(anyFloat())).thenReturn(
new VibratorInfo.Builder(VIBRATOR_ID)
.setCapabilities(capabilities)
.setFrequencyMapping(frequencyMapping)

View File

@@ -93,6 +93,9 @@ import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.verification.VerificationMode;
import java.util.Objects;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -137,20 +140,19 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
private static final int CUSTOM_LIGHT_COLOR = Color.BLACK;
private static final int CUSTOM_LIGHT_ON = 10000;
private static final int CUSTOM_LIGHT_OFF = 10000;
private static final long[] FALLBACK_VIBRATION_PATTERN = new long[] {100, 100, 100};
private static final VibrationEffect FALLBACK_VIBRATION =
VibrationEffect.createWaveform(FALLBACK_VIBRATION_PATTERN, -1);
private static final int MAX_VIBRATION_DELAY = 1000;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
getContext().addMockSystemService(Vibrator.class, mVibrator);
when(mAudioManager.isAudioFocusExclusive()).thenReturn(false);
when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
when(mVibrator.hasFrequencyControl()).thenReturn(false);
long serviceReturnValue = IntPair.of(
AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
@@ -164,13 +166,12 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService = spy(new NotificationManagerService(getContext(), mNotificationRecordLogger,
mNotificationInstanceIdSequence));
mService.setVibratorHelper(new VibratorHelper(getContext()));
mService.setAudioManager(mAudioManager);
mService.setVibrator(mVibrator);
mService.setSystemReady(true);
mService.setHandler(mHandler);
mService.setLights(mLight);
mService.setScreenOn(false);
mService.setFallbackVibrationPattern(FALLBACK_VIBRATION_PATTERN);
mService.setUsageStats(mUsageStats);
mService.setAccessibilityManager(accessibilityManager);
mService.mScreenOn = false;
@@ -416,32 +417,34 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
}
private void verifyVibrate() {
ArgumentCaptor<AudioAttributes> captor = ArgumentCaptor.forClass(AudioAttributes.class);
verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher),
anyString(), captor.capture());
assertEquals(0, (captor.getValue().getAllFlags()
& AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
verifyVibrate(/* times= */ 1);
}
private void verifyVibrate(int times) {
verify(mVibrator, times(times)).vibrate(eq(Process.SYSTEM_UID),
eq(PackageManagerService.PLATFORM_PACKAGE_NAME), any(), anyString(),
any(AudioAttributes.class));
verifyVibrate(mVibrateOnceMatcher, times(times));
}
private void verifyVibrateLooped() {
verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateLoopMatcher),
anyString(), any(AudioAttributes.class));
verifyVibrate(mVibrateLoopMatcher, times(1));
}
private void verifyDelayedVibrateLooped() {
verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
argThat(mVibrateLoopMatcher), anyString(), any(AudioAttributes.class));
verifyVibrate(mVibrateLoopMatcher, timeout(MAX_VIBRATION_DELAY).times(1));
}
private void verifyDelayedVibrate() {
verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
argThat(mVibrateOnceMatcher), anyString(), any(AudioAttributes.class));
private void verifyDelayedVibrate(VibrationEffect effect) {
verifyVibrate(argument -> Objects.equals(effect, argument),
timeout(MAX_VIBRATION_DELAY).times(1));
}
private void verifyVibrate(ArgumentMatcher<VibrationEffect> effectMatcher,
VerificationMode verification) {
ArgumentCaptor<AudioAttributes> captor = ArgumentCaptor.forClass(AudioAttributes.class);
verify(mVibrator, verification).vibrate(eq(Process.SYSTEM_UID),
eq(PackageManagerService.PLATFORM_PACKAGE_NAME), argThat(effectMatcher),
anyString(), captor.capture());
assertEquals(0, (captor.getValue().getAllFlags()
& AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
}
private void verifyStopVibrate() {
@@ -761,11 +764,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.buzzBeepBlinkLocked(r);
VibrationEffect effect = VibrationEffect.createWaveform(r.getVibration(), -1);
verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
eq(effect), anyString(),
(AudioAttributes) anyObject());
verifyDelayedVibrate(r.getVibration());
assertTrue(r.isInterruptive());
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@@ -800,8 +799,8 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.buzzBeepBlinkLocked(r);
verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
eq(FALLBACK_VIBRATION), anyString(), (AudioAttributes) anyObject());
verifyDelayedVibrate(
mService.getVibratorHelper().createFallbackVibration(/* insistent= */ false));
verify(mRingtonePlayer, never()).playAsync
(anyObject(), anyObject(), anyBoolean(), anyObject());
assertTrue(r.isInterruptive());

View File

@@ -37,6 +37,7 @@ import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Build;
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
@@ -63,6 +64,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
@Mock TelecomManager mTm;
@Mock RankingHandler handler;
@Mock PackageManager mPm;
@Mock Vibrator mVibrator;
private final String callPkg = "com.android.server.notification";
private final int callUid = 10;
@@ -98,6 +100,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
when(mContext.getContentResolver()).thenReturn(getContext().getContentResolver());
when(mContext.getPackageManager()).thenReturn(mPm);
when(mContext.getSystemService(eq(Context.TELECOM_SERVICE))).thenReturn(mTm);
when(mContext.getSystemService(Vibrator.class)).thenReturn(mVibrator);
when(mContext.getString(anyInt())).thenCallRealMethod();
when(mContext.getColor(anyInt())).thenCallRealMethod();
when(mTm.getDefaultDialerPackage()).thenReturn(callPkg);

View File

@@ -62,6 +62,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.Adjustment;
import android.service.notification.StatusBarNotification;
@@ -82,7 +83,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -91,6 +91,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
private final Context mMockContext = mock(Context.class);
@Mock private PackageManager mPm;
@Mock private ContentResolver mContentResolver;
@Mock private Vibrator mVibrator;
private final String mPkg = PKG_O;
private final int uid = 9583;
@@ -122,6 +123,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mMockContext.getSystemService(eq(Vibrator.class))).thenReturn(mVibrator);
when(mMockContext.getResources()).thenReturn(getContext().getResources());
when(mMockContext.getPackageManager()).thenReturn(mPm);
when(mMockContext.getContentResolver()).thenReturn(mContentResolver);
@@ -203,6 +205,26 @@ public class NotificationRecordTest extends UiServiceTestCase {
return new StatusBarNotification(mPkg, mPkg, id1, tag1, uid, uid, n, mUser, null, uid);
}
private StatusBarNotification getInsistentNotification(boolean defaultVibration) {
final Builder builder = new Builder(mMockContext)
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setPriority(Notification.PRIORITY_HIGH);
int defaults = 0;
if (defaultVibration) {
defaults |= Notification.DEFAULT_VIBRATE;
} else {
builder.setVibrate(CUSTOM_VIBRATION);
channel.setVibrationPattern(CUSTOM_CHANNEL_VIBRATION);
}
builder.setDefaults(defaults);
builder.setFlag(Notification.FLAG_INSISTENT, true);
Notification n = builder.build();
return new StatusBarNotification(mPkg, mPkg, id1, tag1, uid, uid, n, mUser, null, uid);
}
private StatusBarNotification getMessagingStyleNotification() {
return getMessagingStyleNotification(mPkg);
}
@@ -309,7 +331,8 @@ public class NotificationRecordTest extends UiServiceTestCase {
false /* lights */, false /* defaultLights */, null /* group */);
NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(CUSTOM_VIBRATION, record.getVibration());
assertEquals(VibratorHelper.createWaveformVibration(
CUSTOM_VIBRATION, /* insistent= */ false), record.getVibration());
}
@Test
@@ -322,7 +345,8 @@ public class NotificationRecordTest extends UiServiceTestCase {
false /* lights */, false /* defaultLights */, null /* group */);
NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertTrue(!Arrays.equals(CUSTOM_VIBRATION, record.getVibration()));
assertNotEquals(VibratorHelper.createWaveformVibration(
CUSTOM_VIBRATION, /* insistent= */ false), record.getVibration());
}
@Test
@@ -334,7 +358,18 @@ public class NotificationRecordTest extends UiServiceTestCase {
false /* lights */, false /* defaultLights */, null /* group */);
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(CUSTOM_CHANNEL_VIBRATION, record.getVibration());
assertEquals(VibratorHelper.createWaveformVibration(
CUSTOM_CHANNEL_VIBRATION, /* insistent= */ false), record.getVibration());
}
@Test
public void testVibration_insistent_createsInsistentVibrationEffect() {
channel.enableVibration(true);
StatusBarNotification sbn = getInsistentNotification(false /* defaultBuzz */);
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(VibratorHelper.createWaveformVibration(
CUSTOM_CHANNEL_VIBRATION, /* insistent= */ true), record.getVibration());
}
@Test

View File

@@ -41,6 +41,7 @@ import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Build;
import android.os.UserHandle;
import android.os.Vibrator;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContentResolver;
@@ -84,6 +85,7 @@ public class RankingHelperTest extends UiServiceTestCase {
@Mock Context mContext;
@Mock ZenModeHelper mMockZenModeHelper;
@Mock RankingConfig mConfig;
@Mock Vibrator mVibrator;
private NotificationManager.Policy mTestNotificationPolicy;
private Notification mNotiGroupGSortA;
@@ -125,6 +127,7 @@ public class RankingHelperTest extends UiServiceTestCase {
InstrumentationRegistry.getContext().getContentResolver());
when(mContext.getPackageManager()).thenReturn(mPm);
when(mContext.getApplicationInfo()).thenReturn(legacy);
when(mContext.getSystemService(Vibrator.class)).thenReturn(mVibrator);
TestableContentResolver contentResolver = getContext().getContentResolver();
contentResolver.setFallbackToExisting(false);

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.notification;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.when;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class VibratorHelperTest extends UiServiceTestCase {
private static final long[] CUSTOM_PATTERN = new long[] { 100, 200, 300, 400 };
@Mock private Vibrator mVibrator;
VibratorHelper mVibratorHelper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
getContext().addMockSystemService(Vibrator.class, mVibrator);
mVibratorHelper = new VibratorHelper(getContext());
}
@Test
public void createWaveformVibration_insistent_createsRepeatingVibration() {
assertRepeatingVibration(
VibratorHelper.createWaveformVibration(CUSTOM_PATTERN, /* insistent= */ true));
}
@Test
public void createWaveformVibration_nonInsistent_createsSingleShotVibration() {
assertSingleVibration(
VibratorHelper.createWaveformVibration(CUSTOM_PATTERN, /* insistent= */ false));
}
@Test
public void createWaveformVibration_invalidPattern_returnsNullAndDoesNotCrash() {
assertNull(VibratorHelper.createWaveformVibration(null, false));
assertNull(VibratorHelper.createWaveformVibration(new long[0], false));
assertNull(VibratorHelper.createWaveformVibration(new long[] { 0, 0 }, false));
}
@Test
public void createVibration_insistent_createsRepeatingVibration() {
when(mVibrator.hasFrequencyControl()).thenReturn(false);
assertRepeatingVibration(mVibratorHelper.createDefaultVibration(/* insistent= */ true));
assertRepeatingVibration(mVibratorHelper.createFallbackVibration(/* insistent= */ true));
when(mVibrator.hasFrequencyControl()).thenReturn(true);
assertRepeatingVibration(mVibratorHelper.createDefaultVibration(/* insistent= */ true));
assertRepeatingVibration(mVibratorHelper.createFallbackVibration(/* insistent= */ true));
}
@Test
public void createVibration_nonInsistent_createsSingleShotVibration() {
when(mVibrator.hasFrequencyControl()).thenReturn(false);
assertSingleVibration(mVibratorHelper.createDefaultVibration(/* insistent= */ false));
assertSingleVibration(mVibratorHelper.createFallbackVibration(/* insistent= */ false));
when(mVibrator.hasFrequencyControl()).thenReturn(true);
assertSingleVibration(mVibratorHelper.createDefaultVibration(/* insistent= */ false));
assertSingleVibration(mVibratorHelper.createFallbackVibration(/* insistent= */ false));
}
private void assertRepeatingVibration(VibrationEffect effect) {
assertTrue(getRepeatIndex(effect) >= 0);
}
private void assertSingleVibration(VibrationEffect effect) {
assertEquals(-1, getRepeatIndex(effect));
}
private static int getRepeatIndex(VibrationEffect effect) {
assertTrue("Unknown vibration effect " + effect,
effect instanceof VibrationEffect.Composed);
return ((VibrationEffect.Composed) effect).getRepeatIndex();
}
}