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

Commit 7619c599 authored by Ahmad Khalil's avatar Ahmad Khalil
Browse files

Add adaptive haptics scaling to external vibrations

We've added a new parcelable object, ExternalVibrationScale, which will
hold both vibration scale level and adaptive haptics scale. This will be
returned by onExternalVibrationStart and used when scaling external
vibrations.

Bug: 305957324
Test: atest VibrationScalerTest
Change-Id: If745ca266e1e7f97a9933f7babfb99ba4246380d
parent 67a16df7
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -126,6 +126,7 @@ filegroup {
    srcs: [
        "android/os/IExternalVibrationController.aidl",
        "android/os/IExternalVibratorService.aidl",
        "android/os/ExternalVibrationScale.aidl",
    ],
}

+45 −0
Original line number Diff line number Diff line
/**
 * Copyright (c) 2018, 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 android.os;

/**
 * ExternalVibrationScale holds the vibration scale level and adaptive haptics scale. These
 * can be used to scale external vibrations.
 *
 * @hide
 */
parcelable ExternalVibrationScale {
    @Backing(type="int")
    enum ScaleLevel {
        SCALE_MUTE = -100,
        SCALE_VERY_LOW = -2,
        SCALE_LOW = -1,
        SCALE_NONE = 0,
        SCALE_HIGH = 1,
        SCALE_VERY_HIGH = 2
    }

    /**
     * The scale level that will be applied to external vibrations.
     */
    ScaleLevel scaleLevel = ScaleLevel.SCALE_NONE;

    /**
     * The adaptive haptics scale that will be applied to external vibrations.
     */
    float adaptiveHapticsScale = 1f;
}
+11 −15
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.os;

import android.os.ExternalVibration;
import android.os.ExternalVibrationScale;

/**
 * The communication channel by which an external system that wants to control the system
@@ -32,29 +33,24 @@ import android.os.ExternalVibration;
 * {@hide}
 */
interface IExternalVibratorService {
    const int SCALE_MUTE = -100;
    const int SCALE_VERY_LOW = -2;
    const int SCALE_LOW = -1;
    const int SCALE_NONE = 0;
    const int SCALE_HIGH = 1;
    const int SCALE_VERY_HIGH = 2;

    /**
     * A method called by the external system to start a vibration.
     *
     * If this returns {@code SCALE_MUTE}, then the vibration should <em>not</em> play. If this
     * returns any other scale level, then any currently playing vibration controlled by the
     * requesting system must be muted and this vibration can begin playback.
     * This returns an {@link ExternalVibrationScale} which includes the vibration scale level and
     * the adaptive haptics scale.
     *
     * If the returned scale level is {@link ExternalVibrationScale.ScaleLevel#SCALE_MUTE}, then
     * the vibration should <em>not</em> play. If it returns any other scale level, then
     * any currently playing vibration controlled by the requesting system must be muted and this
     * vibration can begin playback.
     *
     * Note that the IExternalVibratorService implementation will not call mute on any currently
     * playing external vibrations in order to avoid re-entrancy with the system on the other side.
     *
     * @param vibration An ExternalVibration
     *
     * @return {@code SCALE_MUTE} if the external vibration should not play, and any other scale
     *         level if it should.
     * @param vib The external vibration starting.
     * @return {@link ExternalVibrationScale} including scale level and adaptive haptics scale.
     */
    int onExternalVibrationStart(in ExternalVibration vib);
    ExternalVibrationScale onExternalVibrationStart(in ExternalVibration vib);

    /**
     * A method called by the external system when a vibration no longer wants to play.
+31 −11
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ package com.android.server.vibrator;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.vibrator.V1_0.EffectStrength;
import android.os.IExternalVibratorService;
import android.os.ExternalVibrationScale;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -37,11 +37,13 @@ final class VibrationScaler {

    // Scale levels. Each level, except MUTE, is defined as the delta between the current setting
    // and the default intensity for that type of vibration (i.e. current - default).
    private static final int SCALE_VERY_LOW = IExternalVibratorService.SCALE_VERY_LOW; // -2
    private static final int SCALE_LOW = IExternalVibratorService.SCALE_LOW; // -1
    private static final int SCALE_NONE = IExternalVibratorService.SCALE_NONE; // 0
    private static final int SCALE_HIGH = IExternalVibratorService.SCALE_HIGH; // 1
    private static final int SCALE_VERY_HIGH = IExternalVibratorService.SCALE_VERY_HIGH; // 2
    private static final int SCALE_VERY_LOW =
            ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW; // -2
    private static final int SCALE_LOW = ExternalVibrationScale.ScaleLevel.SCALE_LOW; // -1
    private static final int SCALE_NONE = ExternalVibrationScale.ScaleLevel.SCALE_NONE; // 0
    private static final int SCALE_HIGH = ExternalVibrationScale.ScaleLevel.SCALE_HIGH; // 1
    private static final int SCALE_VERY_HIGH =
            ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH; // 2

    // Scale factors for each level.
    private static final float SCALE_FACTOR_VERY_LOW = 0.6f;
@@ -83,9 +85,9 @@ final class VibrationScaler {
     * Calculates the scale to be applied to external vibration with given usage.
     *
     * @param usageHint one of VibrationAttributes.USAGE_*
     * @return one of IExternalVibratorService.SCALE_*
     * @return one of ExternalVibrationScale.ScaleLevel.SCALE_*
     */
    public int getExternalVibrationScale(int usageHint) {
    public int getExternalVibrationScaleLevel(int usageHint) {
        int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint);
        int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);

@@ -106,6 +108,22 @@ final class VibrationScaler {
        }
    }

    /**
     * Returns the adaptive haptics scale that should be applied to the vibrations with
     * the given usage. When no adaptive scales are available for the usages, then returns 1
     * indicating no scaling will be applied
     *
     * @param usageHint one of VibrationAttributes.USAGE_*
     * @return The adaptive haptics scale.
     */
    public float getAdaptiveHapticsScale(int usageHint) {
        if (shouldApplyAdaptiveHapticsScale(usageHint)) {
            return mAdaptiveHapticsScales.get(usageHint);
        }

        return 1f; // no scaling
    }

    /**
     * Scale a {@link VibrationEffect} based on the given usage hint for this vibration.
     *
@@ -152,9 +170,7 @@ final class VibrationScaler {
            }

            // If adaptive haptics scaling is available for this usage, apply it to the segment.
            if (Flags.adaptiveHapticsEnabled()
                    && mAdaptiveHapticsScales.size() > 0
                    && mAdaptiveHapticsScales.contains(usageHint)) {
            if (shouldApplyAdaptiveHapticsScale(usageHint)) {
                float adaptiveScale = mAdaptiveHapticsScales.get(usageHint);
                segment = segment.scaleLinearly(adaptiveScale);
            }
@@ -224,6 +240,10 @@ final class VibrationScaler {
        mAdaptiveHapticsScales.clear();
    }

    private boolean shouldApplyAdaptiveHapticsScale(int usageHint) {
        return Flags.adaptiveHapticsEnabled() && mAdaptiveHapticsScales.contains(usageHint);
    }

    /** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */
    private static int intensityToEffectStrength(int intensity) {
        switch (intensity) {
+24 −10
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.vibrator;

import static android.os.ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
import static android.os.VibrationEffect.VibrationParameter.targetFrequency;

@@ -35,6 +36,7 @@ import android.os.Binder;
import android.os.Build;
import android.os.CombinedVibration;
import android.os.ExternalVibration;
import android.os.ExternalVibrationScale;
import android.os.Handler;
import android.os.IBinder;
import android.os.IExternalVibratorService;
@@ -277,7 +279,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED);

        injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
        if (ServiceManager.isDeclared(VIBRATOR_CONTROL_SERVICE)) {
        if (injector.isServiceDeclared(VIBRATOR_CONTROL_SERVICE)) {
            injector.addService(VIBRATOR_CONTROL_SERVICE, mVibratorControlService);
        }

@@ -1427,6 +1429,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        VibratorControllerHolder createVibratorControllerHolder() {
            return new VibratorControllerHolder();
        }

        boolean isServiceDeclared(String name) {
            return ServiceManager.isDeclared(name);
        }
    }

    /**
@@ -1594,7 +1600,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            IBinder.DeathRecipient {

        public final ExternalVibration externalVibration;
        public int scale;
        public ExternalVibrationScale scale = new ExternalVibrationScale();

        private Vibration.Status mStatus;

@@ -1605,7 +1611,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                    // instead of using DEVICE_ID_INVALID here and relying on the UID checks.
                    Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null));
            this.externalVibration = externalVibration;
            this.scale = IExternalVibratorService.SCALE_NONE;
            mStatus = Vibration.Status.RUNNING;
        }

@@ -1658,7 +1663,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {

        public Vibration.DebugInfo getDebugInfo() {
            return new Vibration.DebugInfo(mStatus, stats, /* playedEffect= */ null,
                    /* originalEffect= */ null, scale, callerInfo);
                    /* originalEffect= */ null, scale.scaleLevel, callerInfo);
        }

        public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
@@ -1988,11 +1993,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
    /** Implementation of {@link IExternalVibratorService} to be triggered on external control. */
    @VisibleForTesting
    final class ExternalVibratorService extends IExternalVibratorService.Stub {
        private static final ExternalVibrationScale SCALE_MUTE = new ExternalVibrationScale();

        static {
            SCALE_MUTE.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
        }

        @Override
        public int onExternalVibrationStart(ExternalVibration vib) {
        public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) {

            if (!hasExternalControlCapability()) {
                return IExternalVibratorService.SCALE_MUTE;
                return SCALE_MUTE;
            }
            if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
                    vib.getUid(), -1 /*owningUid*/, true /*exported*/)
@@ -2000,7 +2011,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
                        + " tried to play externally controlled vibration"
                        + " without VIBRATE permission, ignoring.");
                return IExternalVibratorService.SCALE_MUTE;
                return SCALE_MUTE;
            }

            // Create Vibration.Stats as close to the received request as possible, for tracking.
@@ -2033,7 +2044,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                }

                if (vibrationEndInfo != null) {
                    vibHolder.scale = IExternalVibratorService.SCALE_MUTE;
                    vibHolder.scale = SCALE_MUTE;
                    // Failed to start the vibration, end it and report metrics right away.
                    endVibrationAndWriteStatsLocked(vibHolder, vibrationEndInfo);
                    return vibHolder.scale;
@@ -2074,7 +2085,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                }
                mCurrentExternalVibration = vibHolder;
                vibHolder.linkToDeath();
                vibHolder.scale = mVibrationScaler.getExternalVibrationScale(attrs.getUsage());
                vibHolder.scale.scaleLevel = mVibrationScaler.getExternalVibrationScaleLevel(
                        attrs.getUsage());
                vibHolder.scale.adaptiveHapticsScale = mVibrationScaler.getAdaptiveHapticsScale(
                        attrs.getUsage());
            }

            if (waitForCompletion) {
@@ -2086,7 +2100,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                                new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_CANCELLING),
                                /* continueExternalControl= */ false);
                    }
                    return IExternalVibratorService.SCALE_MUTE;
                    return SCALE_MUTE;
                }
            }
            if (!alreadyUnderExternalControl) {
Loading