Loading services/core/java/com/android/server/VibratorManagerService.java +2 −37 Original line number Diff line number Diff line Loading @@ -27,9 +27,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.IVibratorManagerService; import android.os.Looper; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.ShellCommand; Loading Loading @@ -98,8 +95,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private VibrationSettings mVibrationSettings; private VibrationScaler mVibrationScaler; @GuardedBy("mLock") private boolean mLowPowerMode; static native long nativeInit(); Loading Loading @@ -142,23 +137,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { mVibrationSettings.addListener(this::updateServiceState); PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class); pm.registerLowPowerModeObserver( new PowerManagerInternal.LowPowerModeListener() { @Override public int getServiceType() { return PowerManager.ServiceType.VIBRATION; } @Override public void onLowPowerModeChanged(PowerSaveState result) { synchronized (mLock) { mLowPowerMode = result.batterySaverEnabled; } updateServiceState(); } }); updateServiceState(); } finally { Slog.v(TAG, "VibratorManager service initialized"); Loading Loading @@ -277,7 +255,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { @Nullable private Vibration.Status shouldIgnoreVibrationLocked(int uid, String opPkg, VibrationAttributes attrs) { if (!shouldVibrateForPowerModeLocked(attrs)) { if (!mVibrationSettings.shouldVibrateForPowerMode(attrs.getUsage())) { return Vibration.Status.IGNORED_FOR_POWER; } Loading @@ -286,8 +264,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { return Vibration.Status.IGNORED_FOR_SETTINGS; } if (attrs.getUsage() == VibrationAttributes.USAGE_RINGTONE && !mVibrationSettings.shouldVibrateForRingtone()) { if (!mVibrationSettings.shouldVibrateForRingerMode(attrs.getUsage())) { if (DEBUG) { Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones"); } Loading @@ -309,18 +286,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { return null; } /** Return true is current power mode allows this vibration to happen. */ @GuardedBy("mLock") private boolean shouldVibrateForPowerModeLocked(VibrationAttributes attrs) { if (!mLowPowerMode) { return true; } int usage = attrs.getUsage(); return usage == VibrationAttributes.USAGE_RINGTONE || usage == VibrationAttributes.USAGE_ALARM || usage == VibrationAttributes.USAGE_COMMUNICATION_REQUEST; } /** * Check which mode should be set for a vibration with given {@code uid}, {@code opPkg} and * {@code attrs}. This will return one of the AppOpsManager.MODE_*. Loading services/core/java/com/android/server/VibratorService.java +24 −117 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.server; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.IUidObserver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; Loading @@ -37,9 +36,6 @@ import android.os.IVibratorService; import android.os.IVibratorStateListener; import android.os.Looper; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; Loading @@ -54,7 +50,6 @@ import android.os.Vibrator; import android.os.VibratorInfo; import android.os.WorkSource; import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -95,7 +90,6 @@ public class VibratorService extends IVibratorService.Stub { private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations; private final LinkedList<Vibration.DebugInfo> mPreviousVibrations; private final int mPreviousVibrationsLimit; private final SparseArray<Integer> mProcStatesCache = new SparseArray<>(); private final WorkSource mTmpWorkSource = new WorkSource(); private final Handler mH; private final Object mLock = new Object(); Loading @@ -106,7 +100,6 @@ public class VibratorService extends IVibratorService.Stub { private final AppOpsManager mAppOps; private final IBatteryStats mBatteryStatsService; private final String mSystemUiPackage; private PowerManagerInternal mPowerManagerInternal; private VibrationSettings mVibrationSettings; private VibrationScaler mVibrationScaler; private InputDeviceDelegate mInputDeviceDelegate; Loading @@ -119,30 +112,6 @@ public class VibratorService extends IVibratorService.Stub { private VibrationDeathRecipient mCurrentVibrationDeathRecipient; private int mCurVibUid = -1; private ExternalVibrationHolder mCurrentExternalVibration; private boolean mLowPowerMode; private final IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { mProcStatesCache.put(uid, procState); } @Override public void onUidGone(int uid, boolean disabled) { mProcStatesCache.delete(uid); } @Override public void onUidActive(int uid) { } @Override public void onUidIdle(int uid, boolean disabled) { } @Override public void onUidCachedChanged(int uid, boolean cached) { } }; /** * Implementation of {@link OnVibrationCompleteListener} with a weak reference to this service. Loading Loading @@ -276,37 +245,15 @@ public class VibratorService extends IVibratorService.Stub { mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings); mInputDeviceDelegate = new InputDeviceDelegate(mContext, mH); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); mPowerManagerInternal.registerLowPowerModeObserver( new PowerManagerInternal.LowPowerModeListener() { @Override public int getServiceType() { return ServiceType.VIBRATION; } @Override public void onLowPowerModeChanged(PowerSaveState result) { updateVibrators(); } }); mVibrationSettings.addListener(this::updateVibrators); mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updateVibrators(); mVibrationSettings.updateSettings(); } }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH); try { ActivityManager.getService().registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE, ActivityManager.PROCESS_STATE_UNKNOWN, null); } catch (RemoteException e) { // ignored; both services live in system_server } updateVibrators(); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); Loading Loading @@ -492,9 +439,7 @@ public class VibratorService extends IVibratorService.Stub { return; } if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND && !isNotification(vib) && !isRingtone(vib) && !isAlarm(vib)) { if (!mVibrationSettings.shouldVibrateForUid(uid, vib.attrs.getUsage())) { Slog.e(TAG, "Ignoring incoming vibration as process with" + " uid= " + uid + " is background," + " attrs= " + vib.attrs); Loading Loading @@ -531,16 +476,19 @@ public class VibratorService extends IVibratorService.Stub { private void endVibrationLocked(Vibration vib, Vibration.Status status) { final LinkedList<Vibration.DebugInfo> previousVibrations; if (isRingtone(vib)) { previousVibrations = mPreviousRingVibrations; } else if (isNotification(vib)) { switch (vib.attrs.getUsage()) { case VibrationAttributes.USAGE_NOTIFICATION: previousVibrations = mPreviousNotificationVibrations; } else if (isAlarm(vib)) { break; case VibrationAttributes.USAGE_RINGTONE: previousVibrations = mPreviousRingVibrations; break; case VibrationAttributes.USAGE_ALARM: previousVibrations = mPreviousAlarmVibrations; } else { break; default: previousVibrations = mPreviousVibrations; } if (previousVibrations.size() > mPreviousVibrationsLimit) { previousVibrations.removeFirst(); } Loading Loading @@ -655,17 +603,6 @@ public class VibratorService extends IVibratorService.Stub { } } private boolean shouldVibrateForPowerModeLocked(Vibration vib) { if (!mLowPowerMode) { return true; } int usage = vib.attrs.getUsage(); return usage == VibrationAttributes.USAGE_RINGTONE || usage == VibrationAttributes.USAGE_ALARM || usage == VibrationAttributes.USAGE_COMMUNICATION_REQUEST; } /** Scale the vibration effect by the intensity as appropriate based its intent. */ private void applyVibrationIntensityScalingLocked(Vibration vib) { vib.updateEffect(mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage())); Loading Loading @@ -693,7 +630,7 @@ public class VibratorService extends IVibratorService.Stub { } private boolean shouldVibrate(Vibration vib) { if (!shouldVibrateForPowerModeLocked(vib)) { if (!mVibrationSettings.shouldVibrateForPowerMode(vib.attrs.getUsage())) { endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_POWER); return false; } Loading @@ -704,7 +641,7 @@ public class VibratorService extends IVibratorService.Stub { return false; } if (isRingtone(vib) && !mVibrationSettings.shouldVibrateForRingtone()) { if (!mVibrationSettings.shouldVibrateForRingerMode(vib.attrs.getUsage())) { if (DEBUG) { Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones"); } Loading Loading @@ -770,26 +707,13 @@ public class VibratorService extends IVibratorService.Stub { private void updateVibrators() { synchronized (mLock) { boolean devicesUpdated = mInputDeviceDelegate.updateInputDeviceVibrators( mInputDeviceDelegate.updateInputDeviceVibrators( mVibrationSettings.shouldVibrateInputDevices()); boolean lowPowerModeUpdated = updateLowPowerModeLocked(); if (devicesUpdated || lowPowerModeUpdated) { // If the state changes out from under us then just reset. doCancelVibrateLocked(Vibration.Status.CANCELLED); } } } private boolean updateLowPowerModeLocked() { boolean lowPowerMode = mPowerManagerInternal .getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled; if (lowPowerMode != mLowPowerMode) { mLowPowerMode = lowPowerMode; return true; } return false; } private void doVibratorOn(Vibration vib) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn"); Loading Loading @@ -923,23 +847,10 @@ public class VibratorService extends IVibratorService.Stub { } private static boolean isNotification(Vibration vib) { return vib.attrs.getUsage() == VibrationAttributes.USAGE_NOTIFICATION; } private static boolean isRingtone(Vibration vib) { return vib.attrs.getUsage() == VibrationAttributes.USAGE_RINGTONE; } private static boolean isHapticFeedback(Vibration vib) { return vib.attrs.getUsage() == VibrationAttributes.USAGE_TOUCH; } private static boolean isAlarm(Vibration vib) { return vib.attrs.getUsage() == VibrationAttributes.USAGE_ALARM; private boolean isSystemHapticFeedback(Vibration vib) { if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) { return false; } private boolean isFromSystem(Vibration vib) { return vib.uid == Process.SYSTEM_UID || vib.uid == 0 || mSystemUiPackage.equals(vib.opPkg); } Loading Loading @@ -979,7 +890,6 @@ public class VibratorService extends IVibratorService.Stub { } else { pw.println("null"); } pw.println(" mLowPowerMode=" + mLowPowerMode); pw.println(" mVibratorController=" + mVibratorController); pw.println(" mVibrationSettings=" + mVibrationSettings); pw.println(); Loading Loading @@ -1026,7 +936,6 @@ public class VibratorService extends IVibratorService.Stub { proto.write(VibratorServiceDumpProto.IS_VIBRATING, mVibratorController.isVibrating()); proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL, mVibratorController.isUnderExternalControl()); proto.write(VibratorServiceDumpProto.LOW_POWER_MODE, mLowPowerMode); proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY, mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_TOUCH)); proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY, Loading Loading @@ -1240,9 +1149,7 @@ public class VibratorService extends IVibratorService.Stub { // them. However it may happen that the system is currently playing // haptic feedback as part of the transition. So we don't cancel // system vibrations. if (mCurrentVibration != null && !(isHapticFeedback(mCurrentVibration) && isFromSystem(mCurrentVibration))) { if (mCurrentVibration != null && !isSystemHapticFeedback(mCurrentVibration)) { doCancelVibrateLocked(Vibration.Status.CANCELLED); } } Loading services/core/java/com/android/server/vibrator/VibrationSettings.java +114 −12 Original line number Diff line number Diff line Loading @@ -16,12 +16,18 @@ package com.android.server.vibrator; import android.app.ActivityManager; import android.app.IUidObserver; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; import android.media.AudioManager; import android.net.Uri; import android.os.Handler; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.RemoteException; import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationEffect; Loading @@ -30,7 +36,7 @@ import android.provider.Settings; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import java.util.ArrayList; import java.util.List; Loading @@ -53,6 +59,7 @@ public final class VibrationSettings { private final Vibrator mVibrator; private final AudioManager mAudioManager; private final SettingsObserver mSettingObserver; private final UidObserver mUidObserver; @GuardedBy("mLock") private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>(); Loading @@ -72,12 +79,15 @@ public final class VibrationSettings { private int mNotificationIntensity; @GuardedBy("mLock") private int mRingIntensity; @GuardedBy("mLock") private boolean mLowPowerMode; public VibrationSettings(Context context, Handler handler) { mContext = context; mVibrator = context.getSystemService(Vibrator.class); mAudioManager = context.getSystemService(AudioManager.class); mSettingObserver = new SettingsObserver(handler); mUidObserver = new UidObserver(); registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES)); registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING)); Loading Loading @@ -107,6 +117,35 @@ public final class VibrationSettings { mFallbackEffects.put(VibrationEffect.EFFECT_TEXTURE_TICK, VibrationEffect.get(VibrationEffect.EFFECT_TICK, false)); try { ActivityManager.getService().registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE, ActivityManager.PROCESS_STATE_UNKNOWN, null); } catch (RemoteException e) { // ignored; both services live in system_server } PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class); pm.registerLowPowerModeObserver( new PowerManagerInternal.LowPowerModeListener() { @Override public int getServiceType() { return PowerManager.ServiceType.VIBRATION; } @Override public void onLowPowerModeChanged(PowerSaveState result) { boolean shouldNotifyListeners; synchronized (mLock) { shouldNotifyListeners = result.batterySaverEnabled != mLowPowerMode; mLowPowerMode = result.batterySaverEnabled; } if (shouldNotifyListeners) { notifyListeners(); } } }); // Update with current values from settings. updateSettings(); } Loading Loading @@ -184,12 +223,15 @@ public final class VibrationSettings { } /** * Return {@code true} if the device should vibrate for ringtones. * Return {@code true} if the device should vibrate for current ringer mode. * * <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings * for vibrations while ringing. * for ringtone usage only. All other usages are allowed independently of ringer mode. */ public boolean shouldVibrateForRingtone() { public boolean shouldVibrateForRingerMode(int usageHint) { if (!isRingtone(usageHint)) { return true; } int ringerMode = mAudioManager.getRingerModeInternal(); synchronized (mLock) { if (mVibrateWhenRinging) { Loading @@ -202,6 +244,28 @@ public final class VibrationSettings { } } /** * Returns {@code true} if this vibration is allowed for given {@code uid}. * * <p>This checks if the user is aware of this foreground process, or if the vibration usage is * allowed to play in the background (i.e. it's a notification, ringtone or alarm vibration). */ public boolean shouldVibrateForUid(int uid, int usageHint) { return mUidObserver.isUidForeground(uid) || isNotification(usageHint) || isRingtone(usageHint) || isAlarm(usageHint); } /** * Returns {@code true} if this vibration is allowed for current power mode state. * * <p>This checks if the device is in battery saver mode, in which case only alarm, ringtone and * {@link VibrationAttributes#USAGE_COMMUNICATION_REQUEST} usages are allowed to vibrate. */ public boolean shouldVibrateForPowerMode(int usageHint) { return !mLowPowerMode || isRingtone(usageHint) || isAlarm(usageHint) || usageHint == VibrationAttributes.USAGE_COMMUNICATION_REQUEST; } /** Return {@code true} if input devices should vibrate instead of this device. */ public boolean shouldVibrateInputDevices() { return mVibrateInputDevices; Loading Loading @@ -229,9 +293,7 @@ public final class VibrationSettings { } /** Updates all vibration settings and triggers registered listeners. */ @VisibleForTesting public void updateSettings() { List<OnVibratorSettingsChanged> currentListeners; synchronized (mLock) { mVibrateWhenRinging = getSystemSetting(Settings.System.VIBRATE_WHEN_RINGING, 0) != 0; mApplyRampingRinger = getGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0) != 0; Loading @@ -244,12 +306,8 @@ public final class VibrationSettings { mVibrator.getDefaultRingVibrationIntensity()); mVibrateInputDevices = getSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0; mZenMode = getGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); currentListeners = new ArrayList<>(mListeners); } for (OnVibratorSettingsChanged listener : currentListeners) { listener.onChange(); } notifyListeners(); } @Override Loading @@ -258,7 +316,9 @@ public final class VibrationSettings { + "mVibrateInputDevices=" + mVibrateInputDevices + ", mVibrateWhenRinging=" + mVibrateWhenRinging + ", mApplyRampingRinger=" + mApplyRampingRinger + ", mLowPowerMode=" + mLowPowerMode + ", mZenMode=" + Settings.Global.zenModeToString(mZenMode) + ", mProcStatesCache=" + mUidObserver.mProcStatesCache + ", mHapticFeedbackIntensity=" + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_TOUCH)) + ", mHapticFeedbackDefaultIntensity=" Loading @@ -274,6 +334,16 @@ public final class VibrationSettings { + '}'; } private void notifyListeners() { List<OnVibratorSettingsChanged> currentListeners; synchronized (mLock) { currentListeners = new ArrayList<>(mListeners); } for (OnVibratorSettingsChanged listener : currentListeners) { listener.onChange(); } } private static String intensityToString(int intensity) { switch (intensity) { case Vibrator.VIBRATION_INTENSITY_OFF: Loading Loading @@ -342,4 +412,36 @@ public final class VibrationSettings { updateSettings(); } } /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */ private final class UidObserver extends IUidObserver.Stub { private final SparseArray<Integer> mProcStatesCache = new SparseArray<>(); public boolean isUidForeground(int uid) { return mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; } @Override public void onUidGone(int uid, boolean disabled) { mProcStatesCache.delete(uid); } @Override public void onUidActive(int uid) { } @Override public void onUidIdle(int uid, boolean disabled) { } @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { mProcStatesCache.put(uid, procState); } @Override public void onUidCachedChanged(int uid, boolean cached) { } } } services/tests/servicestests/src/com/android/server/VibratorServiceTest.java +27 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java +12 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.content.Context; import android.content.ContextWrapper; import android.os.Handler; import android.os.IExternalVibratorService; import android.os.PowerManagerInternal; import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationEffect; Loading @@ -40,7 +41,9 @@ import androidx.test.InstrumentationRegistry; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.LocalServices; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; Loading @@ -62,6 +65,7 @@ public class VibrationScalerTest { // TODO(b/131311651): replace with a FakeVibrator instead. @Mock private Vibrator mVibratorMock; @Mock private PowerManagerInternal mPowerManagerInternalMock; private TestLooper mTestLooper; private ContextWrapper mContextSpy; Loading @@ -77,11 +81,19 @@ public class VibrationScalerTest { when(mContextSpy.getContentResolver()).thenReturn(contentResolver); when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock); LocalServices.removeServiceForTest(PowerManagerInternal.class); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); mVibrationSettings = new VibrationSettings( mContextSpy, new Handler(mTestLooper.getLooper())); mVibrationScaler = new VibrationScaler(mContextSpy, mVibrationSettings); } @After public void tearDown() throws Exception { LocalServices.removeServiceForTest(PowerManagerInternal.class); } @Test public void testGetExternalVibrationScale() { when(mVibratorMock.getDefaultHapticFeedbackIntensity()) Loading Loading
services/core/java/com/android/server/VibratorManagerService.java +2 −37 Original line number Diff line number Diff line Loading @@ -27,9 +27,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.IVibratorManagerService; import android.os.Looper; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.ShellCommand; Loading Loading @@ -98,8 +95,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private VibrationSettings mVibrationSettings; private VibrationScaler mVibrationScaler; @GuardedBy("mLock") private boolean mLowPowerMode; static native long nativeInit(); Loading Loading @@ -142,23 +137,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { mVibrationSettings.addListener(this::updateServiceState); PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class); pm.registerLowPowerModeObserver( new PowerManagerInternal.LowPowerModeListener() { @Override public int getServiceType() { return PowerManager.ServiceType.VIBRATION; } @Override public void onLowPowerModeChanged(PowerSaveState result) { synchronized (mLock) { mLowPowerMode = result.batterySaverEnabled; } updateServiceState(); } }); updateServiceState(); } finally { Slog.v(TAG, "VibratorManager service initialized"); Loading Loading @@ -277,7 +255,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { @Nullable private Vibration.Status shouldIgnoreVibrationLocked(int uid, String opPkg, VibrationAttributes attrs) { if (!shouldVibrateForPowerModeLocked(attrs)) { if (!mVibrationSettings.shouldVibrateForPowerMode(attrs.getUsage())) { return Vibration.Status.IGNORED_FOR_POWER; } Loading @@ -286,8 +264,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { return Vibration.Status.IGNORED_FOR_SETTINGS; } if (attrs.getUsage() == VibrationAttributes.USAGE_RINGTONE && !mVibrationSettings.shouldVibrateForRingtone()) { if (!mVibrationSettings.shouldVibrateForRingerMode(attrs.getUsage())) { if (DEBUG) { Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones"); } Loading @@ -309,18 +286,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { return null; } /** Return true is current power mode allows this vibration to happen. */ @GuardedBy("mLock") private boolean shouldVibrateForPowerModeLocked(VibrationAttributes attrs) { if (!mLowPowerMode) { return true; } int usage = attrs.getUsage(); return usage == VibrationAttributes.USAGE_RINGTONE || usage == VibrationAttributes.USAGE_ALARM || usage == VibrationAttributes.USAGE_COMMUNICATION_REQUEST; } /** * Check which mode should be set for a vibration with given {@code uid}, {@code opPkg} and * {@code attrs}. This will return one of the AppOpsManager.MODE_*. Loading
services/core/java/com/android/server/VibratorService.java +24 −117 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.server; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.IUidObserver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; Loading @@ -37,9 +36,6 @@ import android.os.IVibratorService; import android.os.IVibratorStateListener; import android.os.Looper; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; Loading @@ -54,7 +50,6 @@ import android.os.Vibrator; import android.os.VibratorInfo; import android.os.WorkSource; import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -95,7 +90,6 @@ public class VibratorService extends IVibratorService.Stub { private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations; private final LinkedList<Vibration.DebugInfo> mPreviousVibrations; private final int mPreviousVibrationsLimit; private final SparseArray<Integer> mProcStatesCache = new SparseArray<>(); private final WorkSource mTmpWorkSource = new WorkSource(); private final Handler mH; private final Object mLock = new Object(); Loading @@ -106,7 +100,6 @@ public class VibratorService extends IVibratorService.Stub { private final AppOpsManager mAppOps; private final IBatteryStats mBatteryStatsService; private final String mSystemUiPackage; private PowerManagerInternal mPowerManagerInternal; private VibrationSettings mVibrationSettings; private VibrationScaler mVibrationScaler; private InputDeviceDelegate mInputDeviceDelegate; Loading @@ -119,30 +112,6 @@ public class VibratorService extends IVibratorService.Stub { private VibrationDeathRecipient mCurrentVibrationDeathRecipient; private int mCurVibUid = -1; private ExternalVibrationHolder mCurrentExternalVibration; private boolean mLowPowerMode; private final IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { mProcStatesCache.put(uid, procState); } @Override public void onUidGone(int uid, boolean disabled) { mProcStatesCache.delete(uid); } @Override public void onUidActive(int uid) { } @Override public void onUidIdle(int uid, boolean disabled) { } @Override public void onUidCachedChanged(int uid, boolean cached) { } }; /** * Implementation of {@link OnVibrationCompleteListener} with a weak reference to this service. Loading Loading @@ -276,37 +245,15 @@ public class VibratorService extends IVibratorService.Stub { mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings); mInputDeviceDelegate = new InputDeviceDelegate(mContext, mH); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); mPowerManagerInternal.registerLowPowerModeObserver( new PowerManagerInternal.LowPowerModeListener() { @Override public int getServiceType() { return ServiceType.VIBRATION; } @Override public void onLowPowerModeChanged(PowerSaveState result) { updateVibrators(); } }); mVibrationSettings.addListener(this::updateVibrators); mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updateVibrators(); mVibrationSettings.updateSettings(); } }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH); try { ActivityManager.getService().registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE, ActivityManager.PROCESS_STATE_UNKNOWN, null); } catch (RemoteException e) { // ignored; both services live in system_server } updateVibrators(); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); Loading Loading @@ -492,9 +439,7 @@ public class VibratorService extends IVibratorService.Stub { return; } if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND && !isNotification(vib) && !isRingtone(vib) && !isAlarm(vib)) { if (!mVibrationSettings.shouldVibrateForUid(uid, vib.attrs.getUsage())) { Slog.e(TAG, "Ignoring incoming vibration as process with" + " uid= " + uid + " is background," + " attrs= " + vib.attrs); Loading Loading @@ -531,16 +476,19 @@ public class VibratorService extends IVibratorService.Stub { private void endVibrationLocked(Vibration vib, Vibration.Status status) { final LinkedList<Vibration.DebugInfo> previousVibrations; if (isRingtone(vib)) { previousVibrations = mPreviousRingVibrations; } else if (isNotification(vib)) { switch (vib.attrs.getUsage()) { case VibrationAttributes.USAGE_NOTIFICATION: previousVibrations = mPreviousNotificationVibrations; } else if (isAlarm(vib)) { break; case VibrationAttributes.USAGE_RINGTONE: previousVibrations = mPreviousRingVibrations; break; case VibrationAttributes.USAGE_ALARM: previousVibrations = mPreviousAlarmVibrations; } else { break; default: previousVibrations = mPreviousVibrations; } if (previousVibrations.size() > mPreviousVibrationsLimit) { previousVibrations.removeFirst(); } Loading Loading @@ -655,17 +603,6 @@ public class VibratorService extends IVibratorService.Stub { } } private boolean shouldVibrateForPowerModeLocked(Vibration vib) { if (!mLowPowerMode) { return true; } int usage = vib.attrs.getUsage(); return usage == VibrationAttributes.USAGE_RINGTONE || usage == VibrationAttributes.USAGE_ALARM || usage == VibrationAttributes.USAGE_COMMUNICATION_REQUEST; } /** Scale the vibration effect by the intensity as appropriate based its intent. */ private void applyVibrationIntensityScalingLocked(Vibration vib) { vib.updateEffect(mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage())); Loading Loading @@ -693,7 +630,7 @@ public class VibratorService extends IVibratorService.Stub { } private boolean shouldVibrate(Vibration vib) { if (!shouldVibrateForPowerModeLocked(vib)) { if (!mVibrationSettings.shouldVibrateForPowerMode(vib.attrs.getUsage())) { endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_POWER); return false; } Loading @@ -704,7 +641,7 @@ public class VibratorService extends IVibratorService.Stub { return false; } if (isRingtone(vib) && !mVibrationSettings.shouldVibrateForRingtone()) { if (!mVibrationSettings.shouldVibrateForRingerMode(vib.attrs.getUsage())) { if (DEBUG) { Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones"); } Loading Loading @@ -770,26 +707,13 @@ public class VibratorService extends IVibratorService.Stub { private void updateVibrators() { synchronized (mLock) { boolean devicesUpdated = mInputDeviceDelegate.updateInputDeviceVibrators( mInputDeviceDelegate.updateInputDeviceVibrators( mVibrationSettings.shouldVibrateInputDevices()); boolean lowPowerModeUpdated = updateLowPowerModeLocked(); if (devicesUpdated || lowPowerModeUpdated) { // If the state changes out from under us then just reset. doCancelVibrateLocked(Vibration.Status.CANCELLED); } } } private boolean updateLowPowerModeLocked() { boolean lowPowerMode = mPowerManagerInternal .getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled; if (lowPowerMode != mLowPowerMode) { mLowPowerMode = lowPowerMode; return true; } return false; } private void doVibratorOn(Vibration vib) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn"); Loading Loading @@ -923,23 +847,10 @@ public class VibratorService extends IVibratorService.Stub { } private static boolean isNotification(Vibration vib) { return vib.attrs.getUsage() == VibrationAttributes.USAGE_NOTIFICATION; } private static boolean isRingtone(Vibration vib) { return vib.attrs.getUsage() == VibrationAttributes.USAGE_RINGTONE; } private static boolean isHapticFeedback(Vibration vib) { return vib.attrs.getUsage() == VibrationAttributes.USAGE_TOUCH; } private static boolean isAlarm(Vibration vib) { return vib.attrs.getUsage() == VibrationAttributes.USAGE_ALARM; private boolean isSystemHapticFeedback(Vibration vib) { if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) { return false; } private boolean isFromSystem(Vibration vib) { return vib.uid == Process.SYSTEM_UID || vib.uid == 0 || mSystemUiPackage.equals(vib.opPkg); } Loading Loading @@ -979,7 +890,6 @@ public class VibratorService extends IVibratorService.Stub { } else { pw.println("null"); } pw.println(" mLowPowerMode=" + mLowPowerMode); pw.println(" mVibratorController=" + mVibratorController); pw.println(" mVibrationSettings=" + mVibrationSettings); pw.println(); Loading Loading @@ -1026,7 +936,6 @@ public class VibratorService extends IVibratorService.Stub { proto.write(VibratorServiceDumpProto.IS_VIBRATING, mVibratorController.isVibrating()); proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL, mVibratorController.isUnderExternalControl()); proto.write(VibratorServiceDumpProto.LOW_POWER_MODE, mLowPowerMode); proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY, mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_TOUCH)); proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY, Loading Loading @@ -1240,9 +1149,7 @@ public class VibratorService extends IVibratorService.Stub { // them. However it may happen that the system is currently playing // haptic feedback as part of the transition. So we don't cancel // system vibrations. if (mCurrentVibration != null && !(isHapticFeedback(mCurrentVibration) && isFromSystem(mCurrentVibration))) { if (mCurrentVibration != null && !isSystemHapticFeedback(mCurrentVibration)) { doCancelVibrateLocked(Vibration.Status.CANCELLED); } } Loading
services/core/java/com/android/server/vibrator/VibrationSettings.java +114 −12 Original line number Diff line number Diff line Loading @@ -16,12 +16,18 @@ package com.android.server.vibrator; import android.app.ActivityManager; import android.app.IUidObserver; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; import android.media.AudioManager; import android.net.Uri; import android.os.Handler; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.RemoteException; import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationEffect; Loading @@ -30,7 +36,7 @@ import android.provider.Settings; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import java.util.ArrayList; import java.util.List; Loading @@ -53,6 +59,7 @@ public final class VibrationSettings { private final Vibrator mVibrator; private final AudioManager mAudioManager; private final SettingsObserver mSettingObserver; private final UidObserver mUidObserver; @GuardedBy("mLock") private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>(); Loading @@ -72,12 +79,15 @@ public final class VibrationSettings { private int mNotificationIntensity; @GuardedBy("mLock") private int mRingIntensity; @GuardedBy("mLock") private boolean mLowPowerMode; public VibrationSettings(Context context, Handler handler) { mContext = context; mVibrator = context.getSystemService(Vibrator.class); mAudioManager = context.getSystemService(AudioManager.class); mSettingObserver = new SettingsObserver(handler); mUidObserver = new UidObserver(); registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES)); registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING)); Loading Loading @@ -107,6 +117,35 @@ public final class VibrationSettings { mFallbackEffects.put(VibrationEffect.EFFECT_TEXTURE_TICK, VibrationEffect.get(VibrationEffect.EFFECT_TICK, false)); try { ActivityManager.getService().registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE, ActivityManager.PROCESS_STATE_UNKNOWN, null); } catch (RemoteException e) { // ignored; both services live in system_server } PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class); pm.registerLowPowerModeObserver( new PowerManagerInternal.LowPowerModeListener() { @Override public int getServiceType() { return PowerManager.ServiceType.VIBRATION; } @Override public void onLowPowerModeChanged(PowerSaveState result) { boolean shouldNotifyListeners; synchronized (mLock) { shouldNotifyListeners = result.batterySaverEnabled != mLowPowerMode; mLowPowerMode = result.batterySaverEnabled; } if (shouldNotifyListeners) { notifyListeners(); } } }); // Update with current values from settings. updateSettings(); } Loading Loading @@ -184,12 +223,15 @@ public final class VibrationSettings { } /** * Return {@code true} if the device should vibrate for ringtones. * Return {@code true} if the device should vibrate for current ringer mode. * * <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings * for vibrations while ringing. * for ringtone usage only. All other usages are allowed independently of ringer mode. */ public boolean shouldVibrateForRingtone() { public boolean shouldVibrateForRingerMode(int usageHint) { if (!isRingtone(usageHint)) { return true; } int ringerMode = mAudioManager.getRingerModeInternal(); synchronized (mLock) { if (mVibrateWhenRinging) { Loading @@ -202,6 +244,28 @@ public final class VibrationSettings { } } /** * Returns {@code true} if this vibration is allowed for given {@code uid}. * * <p>This checks if the user is aware of this foreground process, or if the vibration usage is * allowed to play in the background (i.e. it's a notification, ringtone or alarm vibration). */ public boolean shouldVibrateForUid(int uid, int usageHint) { return mUidObserver.isUidForeground(uid) || isNotification(usageHint) || isRingtone(usageHint) || isAlarm(usageHint); } /** * Returns {@code true} if this vibration is allowed for current power mode state. * * <p>This checks if the device is in battery saver mode, in which case only alarm, ringtone and * {@link VibrationAttributes#USAGE_COMMUNICATION_REQUEST} usages are allowed to vibrate. */ public boolean shouldVibrateForPowerMode(int usageHint) { return !mLowPowerMode || isRingtone(usageHint) || isAlarm(usageHint) || usageHint == VibrationAttributes.USAGE_COMMUNICATION_REQUEST; } /** Return {@code true} if input devices should vibrate instead of this device. */ public boolean shouldVibrateInputDevices() { return mVibrateInputDevices; Loading Loading @@ -229,9 +293,7 @@ public final class VibrationSettings { } /** Updates all vibration settings and triggers registered listeners. */ @VisibleForTesting public void updateSettings() { List<OnVibratorSettingsChanged> currentListeners; synchronized (mLock) { mVibrateWhenRinging = getSystemSetting(Settings.System.VIBRATE_WHEN_RINGING, 0) != 0; mApplyRampingRinger = getGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0) != 0; Loading @@ -244,12 +306,8 @@ public final class VibrationSettings { mVibrator.getDefaultRingVibrationIntensity()); mVibrateInputDevices = getSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0; mZenMode = getGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); currentListeners = new ArrayList<>(mListeners); } for (OnVibratorSettingsChanged listener : currentListeners) { listener.onChange(); } notifyListeners(); } @Override Loading @@ -258,7 +316,9 @@ public final class VibrationSettings { + "mVibrateInputDevices=" + mVibrateInputDevices + ", mVibrateWhenRinging=" + mVibrateWhenRinging + ", mApplyRampingRinger=" + mApplyRampingRinger + ", mLowPowerMode=" + mLowPowerMode + ", mZenMode=" + Settings.Global.zenModeToString(mZenMode) + ", mProcStatesCache=" + mUidObserver.mProcStatesCache + ", mHapticFeedbackIntensity=" + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_TOUCH)) + ", mHapticFeedbackDefaultIntensity=" Loading @@ -274,6 +334,16 @@ public final class VibrationSettings { + '}'; } private void notifyListeners() { List<OnVibratorSettingsChanged> currentListeners; synchronized (mLock) { currentListeners = new ArrayList<>(mListeners); } for (OnVibratorSettingsChanged listener : currentListeners) { listener.onChange(); } } private static String intensityToString(int intensity) { switch (intensity) { case Vibrator.VIBRATION_INTENSITY_OFF: Loading Loading @@ -342,4 +412,36 @@ public final class VibrationSettings { updateSettings(); } } /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */ private final class UidObserver extends IUidObserver.Stub { private final SparseArray<Integer> mProcStatesCache = new SparseArray<>(); public boolean isUidForeground(int uid) { return mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; } @Override public void onUidGone(int uid, boolean disabled) { mProcStatesCache.delete(uid); } @Override public void onUidActive(int uid) { } @Override public void onUidIdle(int uid, boolean disabled) { } @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { mProcStatesCache.put(uid, procState); } @Override public void onUidCachedChanged(int uid, boolean cached) { } } }
services/tests/servicestests/src/com/android/server/VibratorServiceTest.java +27 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java +12 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.content.Context; import android.content.ContextWrapper; import android.os.Handler; import android.os.IExternalVibratorService; import android.os.PowerManagerInternal; import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationEffect; Loading @@ -40,7 +41,9 @@ import androidx.test.InstrumentationRegistry; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.LocalServices; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; Loading @@ -62,6 +65,7 @@ public class VibrationScalerTest { // TODO(b/131311651): replace with a FakeVibrator instead. @Mock private Vibrator mVibratorMock; @Mock private PowerManagerInternal mPowerManagerInternalMock; private TestLooper mTestLooper; private ContextWrapper mContextSpy; Loading @@ -77,11 +81,19 @@ public class VibrationScalerTest { when(mContextSpy.getContentResolver()).thenReturn(contentResolver); when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock); LocalServices.removeServiceForTest(PowerManagerInternal.class); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); mVibrationSettings = new VibrationSettings( mContextSpy, new Handler(mTestLooper.getLooper())); mVibrationScaler = new VibrationScaler(mContextSpy, mVibrationSettings); } @After public void tearDown() throws Exception { LocalServices.removeServiceForTest(PowerManagerInternal.class); } @Test public void testGetExternalVibrationScale() { when(mVibratorMock.getDefaultHapticFeedbackIntensity()) Loading