diff options
| author | 2019-12-18 06:35:31 +0000 | |
|---|---|---|
| committer | 2019-12-18 06:35:31 +0000 | |
| commit | 197079ffdb5c7392baf23170091889faca25d522 (patch) | |
| tree | f09fd8dd20a9b2199e934a509d2ae063a13424b5 | |
| parent | bc75ee7a6a1734c6af8df65add783d6fc8a09dd2 (diff) | |
| parent | 5d97061d0b9b1fe263d41183d027bb54945757c8 (diff) | |
Merge "vibrator: Support Always-On Effects" am: 65303aa385 am: 5d97061d0b
Change-Id: I1a83e4190a404b94653da68762c2c8acfa8078b9
| -rw-r--r-- | core/java/android/os/IVibratorService.aidl | 1 | ||||
| -rw-r--r-- | core/java/android/os/SystemVibrator.java | 14 | ||||
| -rw-r--r-- | core/java/android/os/Vibrator.java | 19 | ||||
| -rw-r--r-- | core/res/AndroidManifest.xml | 7 | ||||
| -rw-r--r-- | services/core/java/com/android/server/VibratorService.java | 63 | ||||
| -rw-r--r-- | services/core/jni/com_android_server_VibratorService.cpp | 17 |
6 files changed, 121 insertions, 0 deletions
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl index 1456ff7e6c5e..6b881fecad56 100644 --- a/core/java/android/os/IVibratorService.aidl +++ b/core/java/android/os/IVibratorService.aidl @@ -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); diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java index a5188e7cd58d..fbd11ca62a0c 100644 --- a/core/java/android/os/SystemVibrator.java +++ b/core/java/android/os/SystemVibrator.java @@ -70,6 +70,20 @@ public class SystemVibrator extends Vibrator { } @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) { if (mService == null) { diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index 28909c88a734..6456d72a4a6f 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -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; @@ -152,6 +153,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. * * @param milliseconds The number of milliseconds to vibrate. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 8dc84c804055..082424fa4a4f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -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 diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 0db8495e9af9..a1480e305d9e 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -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; @@ -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(); @@ -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) { @@ -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; @@ -988,6 +1028,8 @@ public class VibratorService extends IVibratorService.Stub // If the state changes out from under us then just reset. doCancelVibrateLocked(); } + + updateAlwaysOnLocked(); } } @@ -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(); diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp index dcff5a11aca0..6811e6d0e6f2 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -410,6 +410,21 @@ static jlong vibratorGetCapabilities(JNIEnv*, jclass) { return 0; } +static void vibratorAlwaysOnEnable(JNIEnv* env, jclass, jlong id, jlong effect, jlong strength) { + auto status = halCall(&aidl::IVibrator::alwaysOnEnable, id, + static_cast<aidl::Effect>(effect), static_cast<aidl::EffectStrength>(strength)); + if (!status.isOk()) { + ALOGE("vibratortAlwaysOnEnable command failed (%s).", status.toString8().string()); + } +} + +static void vibratorAlwaysOnDisable(JNIEnv* env, jclass, jlong id) { + auto status = halCall(&aidl::IVibrator::alwaysOnDisable, id); + if (!status.isOk()) { + ALOGE("vibratorAlwaysOnDisable command failed (%s).", status.toString8().string()); + } +} + static const JNINativeMethod method_table[] = { { "vibratorExists", "()Z", (void*)vibratorExists }, { "vibratorInit", "()V", (void*)vibratorInit }, @@ -422,6 +437,8 @@ static const JNINativeMethod method_table[] = { { "vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl}, { "vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl}, { "vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities}, + { "vibratorAlwaysOnEnable", "(JJJ)V", (void*)vibratorAlwaysOnEnable}, + { "vibratorAlwaysOnDisable", "(J)V", (void*)vibratorAlwaysOnDisable}, }; int register_android_server_VibratorService(JNIEnv *env) |