Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0b4fab63 authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

Merge "vibrator: Support Always-On Effects" am: 65303aa3 am: 5d97061d am: 197079ff

Change-Id: I125494e9dcb936f786b8975c9030adf13ef037fa
parents 8e958350 197079ff
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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);
+14 −0
Original line number Diff line number Diff line
@@ -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) {
+19 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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.
     *
+7 −0
Original line number Diff line number Diff line
@@ -1850,6 +1850,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
+63 −0
Original line number Diff line number Diff line
@@ -61,6 +61,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;
@@ -162,6 +163,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();
@@ -173,6 +176,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,
@@ -522,6 +527,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;
@@ -992,6 +1032,8 @@ public class VibratorService extends IVibratorService.Stub
                // If the state changes out from under us then just reset.
                doCancelVibrateLocked();
            }

            updateAlwaysOnLocked();
        }
    }

@@ -1058,6 +1100,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