Loading core/java/android/os/IVibratorService.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ interface IVibratorService { boolean hasVibrator(); boolean hasAmplitudeControl(); boolean setAlwaysOnEffect(int id, in VibrationEffect effect, in AudioAttributes attributes); void vibrate(int uid, String opPkg, in VibrationEffect effect, in AudioAttributes attributes, String reason, IBinder token); void cancelVibrate(IBinder token); Loading core/java/android/os/SystemVibrator.java +14 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,20 @@ public class SystemVibrator extends Vibrator { return false; } @Override public boolean setAlwaysOnEffect(int id, VibrationEffect effect, AudioAttributes attributes) { if (mService == null) { Log.w(TAG, "Failed to set always-on effect; no vibrator service."); return false; } try { return mService.setAlwaysOnEffect(id, effect, attributes); } catch (RemoteException e) { Log.w(TAG, "Failed to set always-on effect.", e); } return false; } @Override public void vibrate(int uid, String opPkg, VibrationEffect effect, String reason, AudioAttributes attributes) { Loading core/java/android/os/Vibrator.java +19 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.os; import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; Loading Loading @@ -151,6 +152,24 @@ public abstract class Vibrator { */ public abstract boolean hasAmplitudeControl(); /** * Configure an always-on haptics effect. * * @param id The board-specific always-on ID to configure. * @param effect Vibration effect to assign to always-on id. Passing null will disable it. * @param attributes {@link AudioAttributes} corresponding to the vibration. For example, * specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or * {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for * vibrations associated with incoming calls. May only be null when effect is null. * @hide */ @RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON) public boolean setAlwaysOnEffect(int id, @Nullable VibrationEffect effect, @Nullable AudioAttributes attributes) { Log.w(TAG, "Always-on effects aren't supported"); return false; } /** * Vibrate constantly for the specified period of time. * Loading core/res/AndroidManifest.xml +7 −0 Original line number Diff line number Diff line Loading @@ -1868,6 +1868,13 @@ android:description="@string/permdesc_vibrate" android:protectionLevel="normal|instant" /> <!-- Allows access to the vibrator always-on settings. <p>Protection level: signature @hide --> <permission android:name="android.permission.VIBRATE_ALWAYS_ON" android:protectionLevel="signature" /> <!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming. <p>Protection level: normal Loading services/core/java/com/android/server/VibratorService.java +63 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.DebugUtils; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.StatsLog; Loading Loading @@ -161,6 +162,8 @@ public class VibratorService extends IVibratorService.Stub private int mHapticFeedbackIntensity; private int mNotificationIntensity; private int mRingIntensity; private SparseArray<Pair<VibrationEffect, AudioAttributes>> mAlwaysOnEffects = new SparseArray<>(); static native boolean vibratorExists(); static native void vibratorInit(); Loading @@ -172,6 +175,8 @@ public class VibratorService extends IVibratorService.Stub static native boolean vibratorSupportsExternalControl(); static native void vibratorSetExternalControl(boolean enabled); static native long vibratorGetCapabilities(); static native void vibratorAlwaysOnEnable(long id, long effect, long strength); static native void vibratorAlwaysOnDisable(long id); private final IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) { Loading Loading @@ -518,6 +523,41 @@ public class VibratorService extends IVibratorService.Stub } } @Override // Binder call public boolean setAlwaysOnEffect(int id, VibrationEffect effect, AudioAttributes attrs) { if (!hasPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)) { throw new SecurityException("Requires VIBRATE_ALWAYS_ON permission"); } if ((mCapabilities & IVibrator.CAP_ALWAYS_ON_CONTROL) == 0) { Slog.e(TAG, "Always-on effects not supported."); return false; } if (effect == null) { synchronized (mLock) { mAlwaysOnEffects.delete(id); vibratorAlwaysOnDisable(id); } } else { if (!verifyVibrationEffect(effect)) { return false; } if (!(effect instanceof VibrationEffect.Prebaked)) { Slog.e(TAG, "Only prebaked effects supported for always-on."); return false; } if (attrs == null) { attrs = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_UNKNOWN) .build(); } synchronized (mLock) { mAlwaysOnEffects.put(id, Pair.create(effect, attrs)); updateAlwaysOnLocked(id, effect, attrs); } } return true; } private void verifyIncomingUid(int uid) { if (uid == Binder.getCallingUid()) { return; Loading Loading @@ -988,6 +1028,8 @@ public class VibratorService extends IVibratorService.Stub // If the state changes out from under us then just reset. doCancelVibrateLocked(); } updateAlwaysOnLocked(); } } Loading Loading @@ -1054,6 +1096,27 @@ public class VibratorService extends IVibratorService.Stub mVibrator.getDefaultRingVibrationIntensity(), UserHandle.USER_CURRENT); } private void updateAlwaysOnLocked(int id, VibrationEffect effect, AudioAttributes attrs) { // TODO: Check DND and LowPower settings final Vibration vib = new Vibration(null, effect, attrs, 0, null, null); final int intensity = getCurrentIntensityLocked(vib); if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) { vibratorAlwaysOnDisable(id); } else { final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect; final int strength = intensityToEffectStrength(intensity); vibratorAlwaysOnEnable(id, prebaked.getId(), strength); } } private void updateAlwaysOnLocked() { for (int i = 0; i < mAlwaysOnEffects.size(); i++) { int id = mAlwaysOnEffects.keyAt(i); Pair<VibrationEffect, AudioAttributes> pair = mAlwaysOnEffects.valueAt(i); updateAlwaysOnLocked(id, pair.first, pair.second); } } @Override public void onInputDeviceAdded(int deviceId) { updateVibrators(); Loading Loading
core/java/android/os/IVibratorService.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ interface IVibratorService { boolean hasVibrator(); boolean hasAmplitudeControl(); boolean setAlwaysOnEffect(int id, in VibrationEffect effect, in AudioAttributes attributes); void vibrate(int uid, String opPkg, in VibrationEffect effect, in AudioAttributes attributes, String reason, IBinder token); void cancelVibrate(IBinder token); Loading
core/java/android/os/SystemVibrator.java +14 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,20 @@ public class SystemVibrator extends Vibrator { return false; } @Override public boolean setAlwaysOnEffect(int id, VibrationEffect effect, AudioAttributes attributes) { if (mService == null) { Log.w(TAG, "Failed to set always-on effect; no vibrator service."); return false; } try { return mService.setAlwaysOnEffect(id, effect, attributes); } catch (RemoteException e) { Log.w(TAG, "Failed to set always-on effect.", e); } return false; } @Override public void vibrate(int uid, String opPkg, VibrationEffect effect, String reason, AudioAttributes attributes) { Loading
core/java/android/os/Vibrator.java +19 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.os; import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; Loading Loading @@ -151,6 +152,24 @@ public abstract class Vibrator { */ public abstract boolean hasAmplitudeControl(); /** * Configure an always-on haptics effect. * * @param id The board-specific always-on ID to configure. * @param effect Vibration effect to assign to always-on id. Passing null will disable it. * @param attributes {@link AudioAttributes} corresponding to the vibration. For example, * specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or * {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for * vibrations associated with incoming calls. May only be null when effect is null. * @hide */ @RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON) public boolean setAlwaysOnEffect(int id, @Nullable VibrationEffect effect, @Nullable AudioAttributes attributes) { Log.w(TAG, "Always-on effects aren't supported"); return false; } /** * Vibrate constantly for the specified period of time. * Loading
core/res/AndroidManifest.xml +7 −0 Original line number Diff line number Diff line Loading @@ -1868,6 +1868,13 @@ android:description="@string/permdesc_vibrate" android:protectionLevel="normal|instant" /> <!-- Allows access to the vibrator always-on settings. <p>Protection level: signature @hide --> <permission android:name="android.permission.VIBRATE_ALWAYS_ON" android:protectionLevel="signature" /> <!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming. <p>Protection level: normal Loading
services/core/java/com/android/server/VibratorService.java +63 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.DebugUtils; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.StatsLog; Loading Loading @@ -161,6 +162,8 @@ public class VibratorService extends IVibratorService.Stub private int mHapticFeedbackIntensity; private int mNotificationIntensity; private int mRingIntensity; private SparseArray<Pair<VibrationEffect, AudioAttributes>> mAlwaysOnEffects = new SparseArray<>(); static native boolean vibratorExists(); static native void vibratorInit(); Loading @@ -172,6 +175,8 @@ public class VibratorService extends IVibratorService.Stub static native boolean vibratorSupportsExternalControl(); static native void vibratorSetExternalControl(boolean enabled); static native long vibratorGetCapabilities(); static native void vibratorAlwaysOnEnable(long id, long effect, long strength); static native void vibratorAlwaysOnDisable(long id); private final IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) { Loading Loading @@ -518,6 +523,41 @@ public class VibratorService extends IVibratorService.Stub } } @Override // Binder call public boolean setAlwaysOnEffect(int id, VibrationEffect effect, AudioAttributes attrs) { if (!hasPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)) { throw new SecurityException("Requires VIBRATE_ALWAYS_ON permission"); } if ((mCapabilities & IVibrator.CAP_ALWAYS_ON_CONTROL) == 0) { Slog.e(TAG, "Always-on effects not supported."); return false; } if (effect == null) { synchronized (mLock) { mAlwaysOnEffects.delete(id); vibratorAlwaysOnDisable(id); } } else { if (!verifyVibrationEffect(effect)) { return false; } if (!(effect instanceof VibrationEffect.Prebaked)) { Slog.e(TAG, "Only prebaked effects supported for always-on."); return false; } if (attrs == null) { attrs = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_UNKNOWN) .build(); } synchronized (mLock) { mAlwaysOnEffects.put(id, Pair.create(effect, attrs)); updateAlwaysOnLocked(id, effect, attrs); } } return true; } private void verifyIncomingUid(int uid) { if (uid == Binder.getCallingUid()) { return; Loading Loading @@ -988,6 +1028,8 @@ public class VibratorService extends IVibratorService.Stub // If the state changes out from under us then just reset. doCancelVibrateLocked(); } updateAlwaysOnLocked(); } } Loading Loading @@ -1054,6 +1096,27 @@ public class VibratorService extends IVibratorService.Stub mVibrator.getDefaultRingVibrationIntensity(), UserHandle.USER_CURRENT); } private void updateAlwaysOnLocked(int id, VibrationEffect effect, AudioAttributes attrs) { // TODO: Check DND and LowPower settings final Vibration vib = new Vibration(null, effect, attrs, 0, null, null); final int intensity = getCurrentIntensityLocked(vib); if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) { vibratorAlwaysOnDisable(id); } else { final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect; final int strength = intensityToEffectStrength(intensity); vibratorAlwaysOnEnable(id, prebaked.getId(), strength); } } private void updateAlwaysOnLocked() { for (int i = 0; i < mAlwaysOnEffects.size(); i++) { int id = mAlwaysOnEffects.keyAt(i); Pair<VibrationEffect, AudioAttributes> pair = mAlwaysOnEffects.valueAt(i); updateAlwaysOnLocked(id, pair.first, pair.second); } } @Override public void onInputDeviceAdded(int deviceId) { updateVibrators(); Loading