Merge "Add new default vibration for notifications" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
37de895517
@@ -212,9 +212,7 @@ import android.os.SystemProperties;
|
|||||||
import android.os.Trace;
|
import android.os.Trace;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.os.VibrationAttributes;
|
|
||||||
import android.os.VibrationEffect;
|
import android.os.VibrationEffect;
|
||||||
import android.os.Vibrator;
|
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.service.notification.Adjustment;
|
import android.service.notification.Adjustment;
|
||||||
@@ -370,12 +368,8 @@ public class NotificationManagerService extends SystemService {
|
|||||||
// 1 second past the ANR timeout.
|
// 1 second past the ANR timeout.
|
||||||
static final int FINISH_TOKEN_TIMEOUT = 11 * 1000;
|
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 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 int INVALID_UID = -1;
|
||||||
static final String ROOT_PKG = "root";
|
static final String ROOT_PKG = "root";
|
||||||
|
|
||||||
@@ -483,7 +477,6 @@ public class NotificationManagerService extends SystemService {
|
|||||||
AudioManagerInternal mAudioManagerInternal;
|
AudioManagerInternal mAudioManagerInternal;
|
||||||
// Can be null for wear
|
// Can be null for wear
|
||||||
@Nullable StatusBarManagerInternal mStatusBar;
|
@Nullable StatusBarManagerInternal mStatusBar;
|
||||||
Vibrator mVibrator;
|
|
||||||
private WindowManagerInternal mWindowManagerInternal;
|
private WindowManagerInternal mWindowManagerInternal;
|
||||||
private AlarmManager mAlarmManager;
|
private AlarmManager mAlarmManager;
|
||||||
private ICompanionDeviceManager mCompanionManager;
|
private ICompanionDeviceManager mCompanionManager;
|
||||||
@@ -505,7 +498,6 @@ public class NotificationManagerService extends SystemService {
|
|||||||
private LogicalLight mNotificationLight;
|
private LogicalLight mNotificationLight;
|
||||||
LogicalLight mAttentionLight;
|
LogicalLight mAttentionLight;
|
||||||
|
|
||||||
private long[] mFallbackVibrationPattern;
|
|
||||||
private boolean mUseAttentionLight;
|
private boolean mUseAttentionLight;
|
||||||
boolean mHasLight = true;
|
boolean mHasLight = true;
|
||||||
boolean mLightEnabled;
|
boolean mLightEnabled;
|
||||||
@@ -584,6 +576,7 @@ public class NotificationManagerService extends SystemService {
|
|||||||
RankingHelper mRankingHelper;
|
RankingHelper mRankingHelper;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
PreferencesHelper mPreferencesHelper;
|
PreferencesHelper mPreferencesHelper;
|
||||||
|
private VibratorHelper mVibratorHelper;
|
||||||
|
|
||||||
private final UserProfiles mUserProfiles = new UserProfiles();
|
private final UserProfiles mUserProfiles = new UserProfiles();
|
||||||
private NotificationListeners mListeners;
|
private NotificationListeners mListeners;
|
||||||
@@ -1598,10 +1591,7 @@ public class NotificationManagerService extends SystemService {
|
|||||||
mVibrateNotificationKey = null;
|
mVibrateNotificationKey = null;
|
||||||
final long identity = Binder.clearCallingIdentity();
|
final long identity = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
// Stop all vibrations with usage of class alarm (ringtone, alarm, notification usages).
|
mVibratorHelper.cancelVibration();
|
||||||
int usageFilter =
|
|
||||||
VibrationAttributes.USAGE_CLASS_ALARM | ~VibrationAttributes.USAGE_CLASS_MASK;
|
|
||||||
mVibrator.cancel(usageFilter);
|
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(identity);
|
Binder.restoreCallingIdentity(identity);
|
||||||
}
|
}
|
||||||
@@ -1995,19 +1985,6 @@ public class NotificationManagerService extends SystemService {
|
|||||||
private SettingsObserver mSettingsObserver;
|
private SettingsObserver mSettingsObserver;
|
||||||
protected ZenModeHelper mZenModeHelper;
|
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) {
|
public NotificationManagerService(Context context) {
|
||||||
this(context,
|
this(context,
|
||||||
new NotificationRecordLoggerImpl(),
|
new NotificationRecordLoggerImpl(),
|
||||||
@@ -2041,13 +2018,18 @@ public class NotificationManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setHints(int hints) {
|
VibratorHelper getVibratorHelper() {
|
||||||
mListenerHints = hints;
|
return mVibratorHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setVibrator(Vibrator vibrator) {
|
void setVibratorHelper(VibratorHelper helper) {
|
||||||
mVibrator = vibrator;
|
mVibratorHelper = helper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setHints(int hints) {
|
||||||
|
mListenerHints = hints;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -2121,11 +2103,6 @@ public class NotificationManagerService extends SystemService {
|
|||||||
mHandler = handler;
|
mHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void setFallbackVibrationPattern(long[] vibrationPattern) {
|
|
||||||
mFallbackVibrationPattern = vibrationPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setPackageManager(IPackageManager packageManager) {
|
void setPackageManager(IPackageManager packageManager) {
|
||||||
mPackageManager = packageManager;
|
mPackageManager = packageManager;
|
||||||
@@ -2200,7 +2177,6 @@ public class NotificationManagerService extends SystemService {
|
|||||||
mPackageManager = packageManager;
|
mPackageManager = packageManager;
|
||||||
mPackageManagerClient = packageManagerClient;
|
mPackageManagerClient = packageManagerClient;
|
||||||
mAppOps = appOps;
|
mAppOps = appOps;
|
||||||
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
|
||||||
mAppUsageStats = appUsageStats;
|
mAppUsageStats = appUsageStats;
|
||||||
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
|
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
|
||||||
mCompanionManager = companionManager;
|
mCompanionManager = companionManager;
|
||||||
@@ -2288,6 +2264,7 @@ public class NotificationManagerService extends SystemService {
|
|||||||
extractorNames);
|
extractorNames);
|
||||||
mSnoozeHelper = snoozeHelper;
|
mSnoozeHelper = snoozeHelper;
|
||||||
mGroupHelper = groupHelper;
|
mGroupHelper = groupHelper;
|
||||||
|
mVibratorHelper = new VibratorHelper(getContext());
|
||||||
mHistoryManager = historyManager;
|
mHistoryManager = historyManager;
|
||||||
|
|
||||||
// This is a ManagedServices object that keeps track of the listeners.
|
// 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);
|
mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
|
||||||
mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
|
mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
|
||||||
|
|
||||||
mFallbackVibrationPattern = getLongArray(resources,
|
|
||||||
R.array.config_notificationFallbackVibePattern,
|
|
||||||
VIBRATE_PATTERN_MAXLEN,
|
|
||||||
DEFAULT_VIBRATE_PATTERN);
|
|
||||||
mInCallNotificationUri = Uri.parse("file://" +
|
mInCallNotificationUri = Uri.parse("file://" +
|
||||||
resources.getString(R.string.config_inCallNotificationSound));
|
resources.getString(R.string.config_inCallNotificationSound));
|
||||||
mInCallNotificationAudioAttributes = new AudioAttributes.Builder()
|
mInCallNotificationAudioAttributes = new AudioAttributes.Builder()
|
||||||
@@ -7439,7 +7412,7 @@ public class NotificationManagerService extends SystemService {
|
|||||||
if (mSystemReady && mAudioManager != null) {
|
if (mSystemReady && mAudioManager != null) {
|
||||||
Uri soundUri = record.getSound();
|
Uri soundUri = record.getSound();
|
||||||
hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
|
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.
|
// Demote sound to vibration if vibration missing & phone in vibration mode.
|
||||||
if (vibration == null
|
if (vibration == null
|
||||||
&& hasValidSound
|
&& hasValidSound
|
||||||
@@ -7447,7 +7420,8 @@ public class NotificationManagerService extends SystemService {
|
|||||||
== AudioManager.RINGER_MODE_VIBRATE)
|
== AudioManager.RINGER_MODE_VIBRATE)
|
||||||
&& mAudioManager.getStreamVolume(
|
&& mAudioManager.getStreamVolume(
|
||||||
AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) == 0) {
|
AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) == 0) {
|
||||||
vibration = mFallbackVibrationPattern;
|
boolean insistent = (record.getFlags() & Notification.FLAG_INSISTENT) != 0;
|
||||||
|
vibration = mVibratorHelper.createFallbackVibration(insistent);
|
||||||
}
|
}
|
||||||
hasValidVibrate = vibration != null;
|
hasValidVibrate = vibration != null;
|
||||||
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
|
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
|
||||||
@@ -7674,23 +7648,12 @@ public class NotificationManagerService extends SystemService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean playVibration(final NotificationRecord record, long[] vibration,
|
private boolean playVibration(final NotificationRecord record, final VibrationEffect effect,
|
||||||
boolean delayVibForSound) {
|
boolean delayVibForSound) {
|
||||||
// Escalate privileges so we can use the vibrator even if the
|
// Escalate privileges so we can use the vibrator even if the
|
||||||
// notifying app does not have the VIBRATE permission.
|
// notifying app does not have the VIBRATE permission.
|
||||||
final long identity = Binder.clearCallingIdentity();
|
final long identity = Binder.clearCallingIdentity();
|
||||||
try {
|
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) {
|
if (delayVibForSound) {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
// delay the vibration by the same amount as the notification sound
|
// 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
|
// to the reason so we can still debug from bugreports
|
||||||
String reason = "Notification (" + record.getSbn().getOpPkg() + " "
|
String reason = "Notification (" + record.getSbn().getOpPkg() + " "
|
||||||
+ record.getSbn().getUid() + ") " + (delayed ? "(Delayed)" : "");
|
+ record.getSbn().getUid() + ") " + (delayed ? "(Delayed)" : "");
|
||||||
mVibrator.vibrate(Process.SYSTEM_UID, PackageManagerService.PLATFORM_PACKAGE_NAME,
|
mVibratorHelper.vibrate(effect, record.getAudioAttributes(), reason);
|
||||||
effect, reason, record.getAudioAttributes());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isNotificationForCurrentUser(NotificationRecord record) {
|
private boolean isNotificationForCurrentUser(NotificationRecord record) {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import android.os.Build;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.os.VibrationEffect;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.service.notification.Adjustment;
|
import android.service.notification.Adjustment;
|
||||||
import android.service.notification.NotificationListenerService;
|
import android.service.notification.NotificationListenerService;
|
||||||
@@ -158,7 +159,7 @@ public final class NotificationRecord {
|
|||||||
private String mUserExplanation;
|
private String mUserExplanation;
|
||||||
private boolean mPreChannelsNotification = true;
|
private boolean mPreChannelsNotification = true;
|
||||||
private Uri mSound;
|
private Uri mSound;
|
||||||
private long[] mVibration;
|
private VibrationEffect mVibration;
|
||||||
private AudioAttributes mAttributes;
|
private AudioAttributes mAttributes;
|
||||||
private NotificationChannel mChannel;
|
private NotificationChannel mChannel;
|
||||||
private ArrayList<String> mPeopleOverride;
|
private ArrayList<String> mPeopleOverride;
|
||||||
@@ -287,29 +288,28 @@ public final class NotificationRecord {
|
|||||||
return light;
|
return light;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long[] calculateVibration() {
|
private VibrationEffect calculateVibration() {
|
||||||
long[] vibration;
|
VibratorHelper helper = new VibratorHelper(mContext);
|
||||||
final long[] defaultVibration = NotificationManagerService.getLongArray(
|
final Notification notification = getSbn().getNotification();
|
||||||
mContext.getResources(),
|
final boolean insistent = (notification.flags & Notification.FLAG_INSISTENT) != 0;
|
||||||
com.android.internal.R.array.config_defaultNotificationVibePattern,
|
VibrationEffect defaultVibration = helper.createDefaultVibration(insistent);
|
||||||
NotificationManagerService.VIBRATE_PATTERN_MAXLEN,
|
VibrationEffect vibration;
|
||||||
NotificationManagerService.DEFAULT_VIBRATE_PATTERN);
|
|
||||||
if (getChannel().shouldVibrate()) {
|
if (getChannel().shouldVibrate()) {
|
||||||
vibration = getChannel().getVibrationPattern() == null
|
vibration = getChannel().getVibrationPattern() == null
|
||||||
? defaultVibration : getChannel().getVibrationPattern();
|
? defaultVibration
|
||||||
|
: helper.createWaveformVibration(getChannel().getVibrationPattern(), insistent);
|
||||||
} else {
|
} else {
|
||||||
vibration = null;
|
vibration = null;
|
||||||
}
|
}
|
||||||
if (mPreChannelsNotification
|
if (mPreChannelsNotification
|
||||||
&& (getChannel().getUserLockedFields()
|
&& (getChannel().getUserLockedFields()
|
||||||
& NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
|
& NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
|
||||||
final Notification notification = getSbn().getNotification();
|
|
||||||
final boolean useDefaultVibrate =
|
final boolean useDefaultVibrate =
|
||||||
(notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
|
(notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
|
||||||
if (useDefaultVibrate) {
|
if (useDefaultVibrate) {
|
||||||
vibration = defaultVibration;
|
vibration = defaultVibration;
|
||||||
} else {
|
} else {
|
||||||
vibration = notification.vibrate;
|
vibration = helper.createWaveformVibration(notification.vibrate, insistent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vibration;
|
return vibration;
|
||||||
@@ -1071,7 +1071,7 @@ public final class NotificationRecord {
|
|||||||
return mSound;
|
return mSound;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long[] getVibration() {
|
public VibrationEffect getVibration() {
|
||||||
return mVibration;
|
return mVibration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,7 +67,7 @@ final class VibratorController {
|
|||||||
mNativeWrapper = nativeWrapper;
|
mNativeWrapper = nativeWrapper;
|
||||||
mNativeWrapper.init(vibratorId, listener);
|
mNativeWrapper.init(vibratorId, listener);
|
||||||
// TODO(b/167947076): load suggested range from config
|
// 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",
|
Preconditions.checkNotNull(mVibratorInfo, "Failed to retrieve data for vibrator %d",
|
||||||
vibratorId);
|
vibratorId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse;
|
|||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyFloat;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
@@ -297,7 +298,7 @@ public class VibratorControllerTest {
|
|||||||
private void mockVibratorCapabilities(int capabilities) {
|
private void mockVibratorCapabilities(int capabilities) {
|
||||||
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
|
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
|
||||||
Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
|
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)
|
new VibratorInfo.Builder(VIBRATOR_ID)
|
||||||
.setCapabilities(capabilities)
|
.setCapabilities(capabilities)
|
||||||
.setFrequencyMapping(frequencyMapping)
|
.setFrequencyMapping(frequencyMapping)
|
||||||
|
|||||||
@@ -93,6 +93,9 @@ import org.mockito.ArgumentMatcher;
|
|||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.mockito.verification.VerificationMode;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@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_COLOR = Color.BLACK;
|
||||||
private static final int CUSTOM_LIGHT_ON = 10000;
|
private static final int CUSTOM_LIGHT_ON = 10000;
|
||||||
private static final int CUSTOM_LIGHT_OFF = 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;
|
private static final int MAX_VIBRATION_DELAY = 1000;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
getContext().addMockSystemService(Vibrator.class, mVibrator);
|
||||||
|
|
||||||
when(mAudioManager.isAudioFocusExclusive()).thenReturn(false);
|
when(mAudioManager.isAudioFocusExclusive()).thenReturn(false);
|
||||||
when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
|
when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
|
||||||
when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
|
when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
|
||||||
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
|
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
|
||||||
when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
|
when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
|
||||||
|
when(mVibrator.hasFrequencyControl()).thenReturn(false);
|
||||||
|
|
||||||
long serviceReturnValue = IntPair.of(
|
long serviceReturnValue = IntPair.of(
|
||||||
AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
|
AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
|
||||||
@@ -164,13 +166,12 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
|
|||||||
|
|
||||||
mService = spy(new NotificationManagerService(getContext(), mNotificationRecordLogger,
|
mService = spy(new NotificationManagerService(getContext(), mNotificationRecordLogger,
|
||||||
mNotificationInstanceIdSequence));
|
mNotificationInstanceIdSequence));
|
||||||
|
mService.setVibratorHelper(new VibratorHelper(getContext()));
|
||||||
mService.setAudioManager(mAudioManager);
|
mService.setAudioManager(mAudioManager);
|
||||||
mService.setVibrator(mVibrator);
|
|
||||||
mService.setSystemReady(true);
|
mService.setSystemReady(true);
|
||||||
mService.setHandler(mHandler);
|
mService.setHandler(mHandler);
|
||||||
mService.setLights(mLight);
|
mService.setLights(mLight);
|
||||||
mService.setScreenOn(false);
|
mService.setScreenOn(false);
|
||||||
mService.setFallbackVibrationPattern(FALLBACK_VIBRATION_PATTERN);
|
|
||||||
mService.setUsageStats(mUsageStats);
|
mService.setUsageStats(mUsageStats);
|
||||||
mService.setAccessibilityManager(accessibilityManager);
|
mService.setAccessibilityManager(accessibilityManager);
|
||||||
mService.mScreenOn = false;
|
mService.mScreenOn = false;
|
||||||
@@ -416,32 +417,34 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void verifyVibrate() {
|
private void verifyVibrate() {
|
||||||
ArgumentCaptor<AudioAttributes> captor = ArgumentCaptor.forClass(AudioAttributes.class);
|
verifyVibrate(/* times= */ 1);
|
||||||
verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher),
|
|
||||||
anyString(), captor.capture());
|
|
||||||
assertEquals(0, (captor.getValue().getAllFlags()
|
|
||||||
& AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyVibrate(int times) {
|
private void verifyVibrate(int times) {
|
||||||
verify(mVibrator, times(times)).vibrate(eq(Process.SYSTEM_UID),
|
verifyVibrate(mVibrateOnceMatcher, times(times));
|
||||||
eq(PackageManagerService.PLATFORM_PACKAGE_NAME), any(), anyString(),
|
|
||||||
any(AudioAttributes.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyVibrateLooped() {
|
private void verifyVibrateLooped() {
|
||||||
verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateLoopMatcher),
|
verifyVibrate(mVibrateLoopMatcher, times(1));
|
||||||
anyString(), any(AudioAttributes.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyDelayedVibrateLooped() {
|
private void verifyDelayedVibrateLooped() {
|
||||||
verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
|
verifyVibrate(mVibrateLoopMatcher, timeout(MAX_VIBRATION_DELAY).times(1));
|
||||||
argThat(mVibrateLoopMatcher), anyString(), any(AudioAttributes.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyDelayedVibrate() {
|
private void verifyDelayedVibrate(VibrationEffect effect) {
|
||||||
verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
|
verifyVibrate(argument -> Objects.equals(effect, argument),
|
||||||
argThat(mVibrateOnceMatcher), anyString(), any(AudioAttributes.class));
|
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() {
|
private void verifyStopVibrate() {
|
||||||
@@ -761,11 +764,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
|
|||||||
|
|
||||||
mService.buzzBeepBlinkLocked(r);
|
mService.buzzBeepBlinkLocked(r);
|
||||||
|
|
||||||
VibrationEffect effect = VibrationEffect.createWaveform(r.getVibration(), -1);
|
verifyDelayedVibrate(r.getVibration());
|
||||||
|
|
||||||
verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
|
|
||||||
eq(effect), anyString(),
|
|
||||||
(AudioAttributes) anyObject());
|
|
||||||
assertTrue(r.isInterruptive());
|
assertTrue(r.isInterruptive());
|
||||||
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
|
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
|
||||||
}
|
}
|
||||||
@@ -800,8 +799,8 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
|
|||||||
|
|
||||||
mService.buzzBeepBlinkLocked(r);
|
mService.buzzBeepBlinkLocked(r);
|
||||||
|
|
||||||
verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
|
verifyDelayedVibrate(
|
||||||
eq(FALLBACK_VIBRATION), anyString(), (AudioAttributes) anyObject());
|
mService.getVibratorHelper().createFallbackVibration(/* insistent= */ false));
|
||||||
verify(mRingtonePlayer, never()).playAsync
|
verify(mRingtonePlayer, never()).playAsync
|
||||||
(anyObject(), anyObject(), anyBoolean(), anyObject());
|
(anyObject(), anyObject(), anyBoolean(), anyObject());
|
||||||
assertTrue(r.isInterruptive());
|
assertTrue(r.isInterruptive());
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import android.content.pm.PackageManager;
|
|||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.os.Vibrator;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.telecom.TelecomManager;
|
import android.telecom.TelecomManager;
|
||||||
@@ -63,6 +64,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
|
|||||||
@Mock TelecomManager mTm;
|
@Mock TelecomManager mTm;
|
||||||
@Mock RankingHandler handler;
|
@Mock RankingHandler handler;
|
||||||
@Mock PackageManager mPm;
|
@Mock PackageManager mPm;
|
||||||
|
@Mock Vibrator mVibrator;
|
||||||
|
|
||||||
private final String callPkg = "com.android.server.notification";
|
private final String callPkg = "com.android.server.notification";
|
||||||
private final int callUid = 10;
|
private final int callUid = 10;
|
||||||
@@ -98,6 +100,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
|
|||||||
when(mContext.getContentResolver()).thenReturn(getContext().getContentResolver());
|
when(mContext.getContentResolver()).thenReturn(getContext().getContentResolver());
|
||||||
when(mContext.getPackageManager()).thenReturn(mPm);
|
when(mContext.getPackageManager()).thenReturn(mPm);
|
||||||
when(mContext.getSystemService(eq(Context.TELECOM_SERVICE))).thenReturn(mTm);
|
when(mContext.getSystemService(eq(Context.TELECOM_SERVICE))).thenReturn(mTm);
|
||||||
|
when(mContext.getSystemService(Vibrator.class)).thenReturn(mVibrator);
|
||||||
when(mContext.getString(anyInt())).thenCallRealMethod();
|
when(mContext.getString(anyInt())).thenCallRealMethod();
|
||||||
when(mContext.getColor(anyInt())).thenCallRealMethod();
|
when(mContext.getColor(anyInt())).thenCallRealMethod();
|
||||||
when(mTm.getDefaultDialerPackage()).thenReturn(callPkg);
|
when(mTm.getDefaultDialerPackage()).thenReturn(callPkg);
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ import android.net.Uri;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.os.Vibrator;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.service.notification.Adjustment;
|
import android.service.notification.Adjustment;
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
@@ -82,7 +83,6 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@@ -91,6 +91,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
|
|||||||
private final Context mMockContext = mock(Context.class);
|
private final Context mMockContext = mock(Context.class);
|
||||||
@Mock private PackageManager mPm;
|
@Mock private PackageManager mPm;
|
||||||
@Mock private ContentResolver mContentResolver;
|
@Mock private ContentResolver mContentResolver;
|
||||||
|
@Mock private Vibrator mVibrator;
|
||||||
|
|
||||||
private final String mPkg = PKG_O;
|
private final String mPkg = PKG_O;
|
||||||
private final int uid = 9583;
|
private final int uid = 9583;
|
||||||
@@ -122,6 +123,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
|
|||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
|
when(mMockContext.getSystemService(eq(Vibrator.class))).thenReturn(mVibrator);
|
||||||
when(mMockContext.getResources()).thenReturn(getContext().getResources());
|
when(mMockContext.getResources()).thenReturn(getContext().getResources());
|
||||||
when(mMockContext.getPackageManager()).thenReturn(mPm);
|
when(mMockContext.getPackageManager()).thenReturn(mPm);
|
||||||
when(mMockContext.getContentResolver()).thenReturn(mContentResolver);
|
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);
|
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() {
|
private StatusBarNotification getMessagingStyleNotification() {
|
||||||
return getMessagingStyleNotification(mPkg);
|
return getMessagingStyleNotification(mPkg);
|
||||||
}
|
}
|
||||||
@@ -309,7 +331,8 @@ public class NotificationRecordTest extends UiServiceTestCase {
|
|||||||
false /* lights */, false /* defaultLights */, null /* group */);
|
false /* lights */, false /* defaultLights */, null /* group */);
|
||||||
|
|
||||||
NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
|
NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
|
||||||
assertEquals(CUSTOM_VIBRATION, record.getVibration());
|
assertEquals(VibratorHelper.createWaveformVibration(
|
||||||
|
CUSTOM_VIBRATION, /* insistent= */ false), record.getVibration());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -322,7 +345,8 @@ public class NotificationRecordTest extends UiServiceTestCase {
|
|||||||
false /* lights */, false /* defaultLights */, null /* group */);
|
false /* lights */, false /* defaultLights */, null /* group */);
|
||||||
|
|
||||||
NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
|
NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
|
||||||
assertTrue(!Arrays.equals(CUSTOM_VIBRATION, record.getVibration()));
|
assertNotEquals(VibratorHelper.createWaveformVibration(
|
||||||
|
CUSTOM_VIBRATION, /* insistent= */ false), record.getVibration());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -334,7 +358,18 @@ public class NotificationRecordTest extends UiServiceTestCase {
|
|||||||
false /* lights */, false /* defaultLights */, null /* group */);
|
false /* lights */, false /* defaultLights */, null /* group */);
|
||||||
|
|
||||||
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
|
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
|
@Test
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import android.media.AudioAttributes;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.os.Vibrator;
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
import android.testing.TestableContentResolver;
|
import android.testing.TestableContentResolver;
|
||||||
@@ -84,6 +85,7 @@ public class RankingHelperTest extends UiServiceTestCase {
|
|||||||
@Mock Context mContext;
|
@Mock Context mContext;
|
||||||
@Mock ZenModeHelper mMockZenModeHelper;
|
@Mock ZenModeHelper mMockZenModeHelper;
|
||||||
@Mock RankingConfig mConfig;
|
@Mock RankingConfig mConfig;
|
||||||
|
@Mock Vibrator mVibrator;
|
||||||
|
|
||||||
private NotificationManager.Policy mTestNotificationPolicy;
|
private NotificationManager.Policy mTestNotificationPolicy;
|
||||||
private Notification mNotiGroupGSortA;
|
private Notification mNotiGroupGSortA;
|
||||||
@@ -125,6 +127,7 @@ public class RankingHelperTest extends UiServiceTestCase {
|
|||||||
InstrumentationRegistry.getContext().getContentResolver());
|
InstrumentationRegistry.getContext().getContentResolver());
|
||||||
when(mContext.getPackageManager()).thenReturn(mPm);
|
when(mContext.getPackageManager()).thenReturn(mPm);
|
||||||
when(mContext.getApplicationInfo()).thenReturn(legacy);
|
when(mContext.getApplicationInfo()).thenReturn(legacy);
|
||||||
|
when(mContext.getSystemService(Vibrator.class)).thenReturn(mVibrator);
|
||||||
TestableContentResolver contentResolver = getContext().getContentResolver();
|
TestableContentResolver contentResolver = getContext().getContentResolver();
|
||||||
contentResolver.setFallbackToExisting(false);
|
contentResolver.setFallbackToExisting(false);
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user