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

Commit 73122b7a authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Imlement vibrate methods in DefaultHalVibrator" into main

parents 30f05085 bf74271b
Loading
Loading
Loading
Loading
+63 −2
Original line number Diff line number Diff line
@@ -18,9 +18,13 @@ package com.android.server.vibrator;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.vibrator.CompositeEffect;
import android.hardware.vibrator.CompositePwleV2;
import android.hardware.vibrator.IVibrationSession;
import android.hardware.vibrator.IVibrator;
import android.hardware.vibrator.IVibratorManager;
import android.hardware.vibrator.PrimitivePwle;
import android.hardware.vibrator.VendorEffect;

/** Handles interactions with vibrator HAL services through native. */
interface HalNativeHandler {
@@ -56,7 +60,64 @@ interface HalNativeHandler {
     * <p>This should only be called if HAL has {@link IVibrator#CAP_ON_CALLBACK}. The HAL might
     * fail the request otherwise.
     *
     * @return true if successful, false otherwise.
     * @return durationMs if successful, zero if unsupported or negative if failed.
     */
    int vibrateWithCallback(int vibratorId, long vibrationId, long stepId, int durationMs);

    /**
     * Call {@link IVibrator#performVendorEffect} on single vibrator using vibration id for
     * callbacks from HAL.
     *
     * <p>This should only be called if HAL has {@link IVibrator#CAP_PERFORM_VENDOR_EFFECTS}.
     * The HAL might fail the request otherwise.
     *
     * @return max int value if successful, zero if unsupported or negative if failed.
     */
    int vibrateWithCallback(int vibratorId, long vibrationId, long stepId, VendorEffect effect);

    /**
     * Call {@link IVibrator#perform} on single vibrator using vibration id for callbacks from HAL.
     *
     * <p>This should only be called if HAL has {@link IVibrator#CAP_PERFORM_CALLBACK}. The HAL
     * might fail the request otherwise.
     *
     * @return duration (milliseconds) if successful, zero if unsupported or negative if failed.
     */
    int vibrateWithCallback(int vibratorId, long vibrationId, long stepId, int effectId,
            int effectStrength);

    /**
     * Call {@link IVibrator#compose} on single vibrator using vibration id for callbacks from HAL.
     *
     * <p>This should only be called if HAL has {@link IVibrator#CAP_COMPOSE_EFFECTS}. The HAL
     * might fail the request otherwise.
     *
     * @return max int value if successful, zero if unsupported or negative if failed.
     */
    int vibrateWithCallback(int vibratorId, long vibrationId, long stepId,
            CompositeEffect[] effects);

    /**
     * Call {@link IVibrator#composePwle} on single vibrator using vibration id for callbacks
     * from HAL.
     *
     * <p>This should only be called if HAL has {@link IVibrator#CAP_COMPOSE_PWLE_EFFECTS}. The HAL
     * might fail the request otherwise.
     *
     * @return max int value if successful, zero if unsupported or negative if failed.
     */
    int vibrateWithCallback(int vibratorId, long vibrationId, long stepId,
            PrimitivePwle[] effects);

    /**
     * Call {@link IVibrator#composePwleV2} on single vibrator using vibration id for callbacks
     * from HAL.
     *
     * <p>This should only be called if HAL has {@link IVibrator#CAP_COMPOSE_PWLE_EFFECTS_V2}.
     * The HAL might fail the request otherwise.
     *
     * @return max int value if successful, zero if unsupported or negative if failed.
     */
    boolean vibrateWithCallback(int vibratorId, long vibrationId, long stepId, int durationMs);
    int vibrateWithCallback(int vibratorId, long vibrationId, long stepId,
            CompositePwleV2 composite);
}
+100 −2
Original line number Diff line number Diff line
@@ -34,8 +34,12 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.vibrator.CompositeEffect;
import android.hardware.vibrator.CompositePwleV2;
import android.hardware.vibrator.IVibrator;
import android.hardware.vibrator.IVibratorManager;
import android.hardware.vibrator.PrimitivePwle;
import android.hardware.vibrator.VendorEffect;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
@@ -49,6 +53,7 @@ import android.os.IExternalVibratorService;
import android.os.IVibratorManagerService;
import android.os.IVibratorStateListener;
import android.os.Looper;
import android.os.Parcel;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -253,9 +258,29 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            int[] vibratorIds);

    /** Calls {@link IVibrator#on} with callback. */
    private static native boolean nativeVibratorOnWithCallback(long nativePtr, int vibratorId,
    private static native int nativeVibratorOnWithCallback(long nativePtr, int vibratorId,
            long vibrationId, long stepId, int durationMs);

    /** Calls {@link IVibrator#performVendorEffect} with given {@link VendorEffect} and callback. */
    private static native int nativeVibratorPerformVendorEffectWithCallback(long nativePtr,
            int vibratorId, long vibrationId, long stepId, Parcel effect);

    /** Calls {@link IVibrator#perform} with callback. */
    private static native int nativeVibratorPerformEffectWithCallback(long nativePtr,
            int vibratorId, long vibrationId, long stepId, int effectId, int effectStrength);

    /** Calls {@link IVibrator#compose} with given {@link CompositeEffect} array and callback. */
    private static native int nativeVibratorComposeEffectWithCallback(long nativePtr,
            int vibratorId, long vibrationId, long stepId, Parcel effect);

    /** Calls {@link IVibrator#composePwle} with given {@link PrimitivePwle} array and callback. */
    private static native int nativeVibratorComposePwleEffectWithCallback(long nativePtr,
            int vibratorId, long vibrationId, long stepId, Parcel effect);

    /** Calls {@link IVibrator#composePwleV2} with callback. */
    private static native int nativeVibratorComposePwleV2EffectWithCallback(long nativePtr,
            int vibratorId, long vibrationId, long stepId, Parcel effect);

    // TODO(b/409002423): remove native methods below once remove_hidl_support flag removed
    static native long nativeInit(HalVibratorManager.Callbacks callback);

@@ -2377,11 +2402,84 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        }

        @Override
        public boolean vibrateWithCallback(int vibratorId, long vibrationId, long stepId,
        public int vibrateWithCallback(int vibratorId, long vibrationId, long stepId,
                int durationMs) {
            return nativeVibratorOnWithCallback(mNativePtr, vibratorId, vibrationId, stepId,
                    durationMs);
        }

        @Override
        public int vibrateWithCallback(int vibratorId, long vibrationId, long stepId,
                VendorEffect effect) {
            Parcel parcel = Parcel.obtain();
            try {
                effect.writeToParcel(parcel, /* flags= */ 0);
                parcel.setDataPosition(0);
                return nativeVibratorPerformVendorEffectWithCallback(mNativePtr, vibratorId,
                        vibrationId, stepId, parcel);
            } finally {
                parcel.recycle();
                Trace.traceEnd(TRACE_TAG_VIBRATOR);
            }
        }

        @Override
        public int vibrateWithCallback(int vibratorId, long vibrationId, long stepId,
                int effectId, int effectStrength) {
            return nativeVibratorPerformEffectWithCallback(mNativePtr, vibratorId, vibrationId,
                    stepId, effectId, effectStrength);
        }

        @Override
        public int vibrateWithCallback(int vibratorId, long vibrationId, long stepId,
                CompositeEffect[] effects) {
            Parcel parcel = Parcel.obtain();
            try {
                parcel.writeInt(effects.length);
                for (CompositeEffect effect : effects) {
                    effect.writeToParcel(parcel, /* flags= */ 0);
                }
                parcel.setDataPosition(0);
                return nativeVibratorComposeEffectWithCallback(mNativePtr, vibratorId,
                        vibrationId, stepId, parcel);
            } finally {
                parcel.recycle();
                Trace.traceEnd(TRACE_TAG_VIBRATOR);
            }
        }

        @Override
        public int vibrateWithCallback(int vibratorId, long vibrationId, long stepId,
                PrimitivePwle[] effects) {
            Parcel parcel = Parcel.obtain();
            try {
                parcel.writeInt(effects.length);
                for (PrimitivePwle effect : effects) {
                    effect.writeToParcel(parcel, /* flags= */ 0);
                }
                parcel.setDataPosition(0);
                return nativeVibratorComposePwleEffectWithCallback(mNativePtr, vibratorId,
                        vibrationId, stepId, parcel);
            } finally {
                parcel.recycle();
                Trace.traceEnd(TRACE_TAG_VIBRATOR);
            }
        }

        @Override
        public int vibrateWithCallback(int vibratorId, long vibrationId, long stepId,
                CompositePwleV2 composite) {
            Parcel parcel = Parcel.obtain();
            try {
                composite.writeToParcel(parcel, /* flags= */ 0);
                parcel.setDataPosition(0);
                return nativeVibratorComposePwleV2EffectWithCallback(mNativePtr, vibratorId,
                        vibrationId, stepId, parcel);
            } finally {
                parcel.recycle();
                Trace.traceEnd(TRACE_TAG_VIBRATOR);
            }
        }
    }

    /** Keep records of vibrations played and provide debug information for this service. */
+162 −16
Original line number Diff line number Diff line
@@ -22,8 +22,15 @@ import static android.os.VibrationEffect.effectStrengthToString;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.vibrator.ActivePwle;
import android.hardware.vibrator.CompositeEffect;
import android.hardware.vibrator.CompositePwleV2;
import android.hardware.vibrator.FrequencyAccelerationMapEntry;
import android.hardware.vibrator.IVibrator;
import android.hardware.vibrator.PrimitivePwle;
import android.hardware.vibrator.PwleV2Primitive;
import android.hardware.vibrator.VendorEffect;
import android.os.BadParcelableException;
import android.os.Binder;
import android.os.Handler;
import android.os.IVibratorStateListener;
@@ -45,6 +52,7 @@ import com.android.server.vibrator.VintfUtils.VintfSupplier;

import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;

/** Implementations for {@link HalVibrator} backed by VINTF objects. */
@@ -246,7 +254,7 @@ class VintfHalVibrator {
            Trace.traceBegin(TRACE_TAG_VIBRATOR, "DefaultHalVibrator#on");
            try {
                synchronized (mLock) {
                    boolean result;
                    int result;
                    if (mVibratorInfo.hasCapability(IVibrator.CAP_ON_CALLBACK)) {
                        // Delegate vibrate with callback to native, to avoid creating a new
                        // callback instance for each call, overloading the GC.
@@ -255,19 +263,19 @@ class VintfHalVibrator {
                    } else {
                        // Vibrate callback not supported, avoid unnecessary JNI round trip and
                        // simulate HAL callback here using a Handler.
                        result = VintfUtils.runNoThrow(mHalSupplier,
                        result = vibrateNoThrow(
                                hal -> hal.on((int) milliseconds, null),
                                (int) milliseconds,
                                e -> logError("Error turning on for " + milliseconds + "ms", e));
                        if (result) {
                        if (result > 0) {
                            mHandler.postDelayed(newVibrationCallback(vibrationId, stepId),
                                    milliseconds);
                        }
                    }
                    if (result) {
                    if (result > 0) {
                        updateStateAndNotifyListenersLocked(State.VIBRATING);
                    }
                    // IVibrator.on API should never be unsupported.
                    return result ? milliseconds : -1;
                    return result > 0 ? milliseconds : result;
                }
            } finally {
                Trace.traceEnd(TRACE_TAG_VIBRATOR);
@@ -278,8 +286,25 @@ class VintfHalVibrator {
        public long on(long vibrationId, long stepId, VibrationEffect.VendorEffect effect) {
            Trace.traceBegin(TRACE_TAG_VIBRATOR, "DefaultHalVibrator#on (vendor)");
            try {
                // TODO(b/422944962): implement
                synchronized (mLock) {
                    if (!mVibratorInfo.hasCapability(IVibrator.CAP_PERFORM_VENDOR_EFFECTS)) {
                        return 0;
                    }
                    // Delegate vibrate with callback to native, to avoid creating a new
                    // callback instance for each call, overloading the GC.
                    VendorEffect vendorEffect = new VendorEffect();
                    vendorEffect.vendorData = effect.getVendorData();
                    vendorEffect.vendorScale = effect.getAdaptiveScale();
                    vendorEffect.scale = effect.getScale();
                    vendorEffect.strength = (byte) effect.getEffectStrength();
                    int result = mNativeHandler.vibrateWithCallback(mVibratorId, vibrationId,
                            stepId, vendorEffect);
                    if (result > 0) {
                        updateStateAndNotifyListenersLocked(State.VIBRATING);
                    }
                    // Vendor effect durations are unknown to the framework.
                    return result > 0 ? Long.MAX_VALUE : result;
                }
            } finally {
                Trace.traceEnd(TRACE_TAG_VIBRATOR);
            }
@@ -289,8 +314,33 @@ class VintfHalVibrator {
        public long on(long vibrationId, long stepId, PrebakedSegment prebaked) {
            Trace.traceBegin(TRACE_TAG_VIBRATOR, "DefaultHalVibrator#on (prebaked)");
            try {
                // TODO(b/422944962): implement
                return 0;
                synchronized (mLock) {
                    int result;
                    if (mVibratorInfo.hasCapability(IVibrator.CAP_PERFORM_CALLBACK)) {
                        // Delegate vibrate with callback to native, to avoid creating a new
                        // callback instance for each call, overloading the GC.
                        result = mNativeHandler.vibrateWithCallback(mVibratorId, vibrationId,
                                stepId, prebaked.getEffectId(), prebaked.getEffectStrength());
                    } else {
                        // Vibrate callback not supported, avoid unnecessary JNI round trip and
                        // simulate HAL callback here using a Handler.
                        int effectId = prebaked.getEffectId();
                        byte strength = (byte) prebaked.getEffectStrength();
                        result = vibrateNoThrow(
                                hal -> hal.perform(effectId, strength, null),
                                e -> logError("Error performing effect "
                                        + VibrationEffect.effectIdToString(effectId)
                                        + " with strength "
                                        + VibrationEffect.effectStrengthToString(strength), e));
                        if (result > 0) {
                            mHandler.postDelayed(newVibrationCallback(vibrationId, stepId), result);
                        }
                    }
                    if (result > 0) {
                        updateStateAndNotifyListenersLocked(State.VIBRATING);
                    }
                    return result;
                }
            } finally {
                Trace.traceEnd(TRACE_TAG_VIBRATOR);
            }
@@ -300,8 +350,32 @@ class VintfHalVibrator {
        public long on(long vibrationId, long stepId, PrimitiveSegment[] primitives) {
            Trace.traceBegin(TRACE_TAG_VIBRATOR, "DefaultHalVibrator#on (primitives)");
            try {
                // TODO(b/422944962): implement
                synchronized (mLock) {
                    if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
                        return 0;
                    }
                    // Delegate vibrate with callback to native, to avoid creating a new
                    // callback instance for each call, overloading the GC.
                    CompositeEffect[] effects = new CompositeEffect[primitives.length];
                    long durationMs = 0;
                    for (int i = 0; i < primitives.length; i++) {
                        effects[i] = new CompositeEffect();
                        effects[i].primitive = primitives[i].getPrimitiveId();
                        effects[i].scale = primitives[i].getScale();
                        effects[i].delayMs = primitives[i].getDelay();
                        durationMs += mVibratorInfo.getPrimitiveDuration(effects[i].primitive);
                        durationMs += effects[i].delayMs;
                    }
                    int result = mNativeHandler.vibrateWithCallback(mVibratorId, vibrationId,
                            stepId, effects);
                    if (result > 0) {
                        updateStateAndNotifyListenersLocked(State.VIBRATING);
                    }
                    return result > 0 ? durationMs : result;
                }
            } catch (BadParcelableException e) {
                logError("Error sending parcelable to JNI", e);
                return -1;
            } finally {
                Trace.traceEnd(TRACE_TAG_VIBRATOR);
            }
@@ -311,8 +385,34 @@ class VintfHalVibrator {
        public long on(long vibrationId, long stepId, RampSegment[] primitives) {
            Trace.traceBegin(TRACE_TAG_VIBRATOR, "DefaultHalVibrator#on (pwle v1)");
            try {
                // TODO(b/422944962): implement
                synchronized (mLock) {
                    if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
                        return 0;
                    }
                    // Delegate vibrate with callback to native, to avoid creating a new
                    // callback instance for each call, overloading the GC.
                    PrimitivePwle[] effects = new PrimitivePwle[primitives.length];
                    long durationMs = 0;
                    for (int i = 0; i < primitives.length; i++) {
                        ActivePwle pwle = new ActivePwle();
                        pwle.startAmplitude = primitives[i].getStartAmplitude();
                        pwle.startFrequency = primitives[i].getStartFrequencyHz();
                        pwle.endAmplitude = primitives[i].getEndAmplitude();
                        pwle.endFrequency = primitives[i].getEndFrequencyHz();
                        pwle.duration = (int) primitives[i].getDuration();
                        effects[i] = PrimitivePwle.active(pwle);
                        durationMs += pwle.duration;
                    }
                    int result = mNativeHandler.vibrateWithCallback(mVibratorId, vibrationId,
                            stepId, effects);
                    if (result > 0) {
                        updateStateAndNotifyListenersLocked(State.VIBRATING);
                    }
                    return result > 0 ? durationMs : result;
                }
            } catch (BadParcelableException e) {
                logError("Error sending parcelable to JNI", e);
                return -1;
            } finally {
                Trace.traceEnd(TRACE_TAG_VIBRATOR);
            }
@@ -322,8 +422,32 @@ class VintfHalVibrator {
        public long on(long vibrationId, long stepId, PwlePoint[] pwlePoints) {
            Trace.traceBegin(TRACE_TAG_VIBRATOR, "DefaultHalVibrator#on (pwle v2)");
            try {
                // TODO(b/422944962): implement
                synchronized (mLock) {
                    if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2)) {
                        return 0;
                    }
                    // Delegate vibrate with callback to native, to avoid creating a new
                    // callback instance for each call, overloading the GC.
                    CompositePwleV2 composite = new CompositePwleV2();
                    composite.pwlePrimitives = new PwleV2Primitive[pwlePoints.length];
                    long durationMs = 0;
                    for (int i = 0; i < pwlePoints.length; i++) {
                        composite.pwlePrimitives[i] = new PwleV2Primitive();
                        composite.pwlePrimitives[i].amplitude = pwlePoints[i].getAmplitude();
                        composite.pwlePrimitives[i].frequencyHz = pwlePoints[i].getFrequencyHz();
                        composite.pwlePrimitives[i].timeMillis = pwlePoints[i].getTimeMillis();
                        durationMs += pwlePoints[i].getTimeMillis();
                    }
                    int result = mNativeHandler.vibrateWithCallback(mVibratorId, vibrationId,
                            stepId, composite);
                    if (result > 0) {
                        updateStateAndNotifyListenersLocked(State.VIBRATING);
                    }
                    return result > 0 ? durationMs : result;
                }
            } catch (BadParcelableException e) {
                logError("Error sending parcelable to JNI", e);
                return -1;
            } finally {
                Trace.traceEnd(TRACE_TAG_VIBRATOR);
            }
@@ -399,6 +523,28 @@ class VintfHalVibrator {
            }
        }

        private int vibrateNoThrow(VintfUtils.VintfRunnable<IVibrator> fn, int successResult,
                Consumer<Throwable> errorHandler) {
            return vibrateNoThrow(
                    hal -> {
                        fn.run(hal);
                        return successResult;
                    }, errorHandler);
        }

        private int vibrateNoThrow(VintfUtils.VintfGetter<IVibrator, Integer> fn,
                Consumer<Throwable> errorHandler) {
            try {
                return VintfUtils.get(mHalSupplier, fn);
            } catch (RuntimeException e) {
                errorHandler.accept(e);
                if (e instanceof UnsupportedOperationException) {
                    return 0;
                }
                return -1;
            }
        }

        private Runnable newVibrationCallback(long vibrationId, long stepId) {
            return () -> mCallbacks.onVibrationStepComplete(mVibratorId, vibrationId, stepId);
        }
+127 −16

File changed.

Preview size limit exceeded, changes collapsed.

+51 −0
Original line number Diff line number Diff line
@@ -22,11 +22,14 @@
#include <aidl/android/hardware/vibrator/IVibratorManager.h>
#include <android-base/thread_annotations.h>
#include <android/binder_manager.h>
#include <android/binder_parcel.h>
#include <android/binder_parcel_jni.h>
#include <utils/Log.h>
#include <vibratorservice/VibratorManagerHalController.h>

#include <condition_variable>
#include <mutex>
#include <vector>

#include "core_jni_helpers.h"
#include "jni.h"
@@ -175,6 +178,54 @@ std::unique_ptr<HalProvider<I>> defaultProviderForDeclaredService() {
    return nullptr;
}

// Returns a new parcelable from given java parcel object.
template <typename I>
I fromParcel(JNIEnv* env, AParcel* parcel) {
    I parcelable;
    if (binder_status_t status = parcelable.readFromParcel(parcel); status != STATUS_OK) {
        jniThrowExceptionFmt(env, "android/os/BadParcelableException",
                             "Failed to readFromParcel, status %d (%s)", status, strerror(-status));
    }
    return parcelable;
}

// Returns a new parcelable from given java parcel object.
template <typename I>
I fromJavaParcel(JNIEnv* env, jobject data) {
    I parcelable;
    if (AParcel* parcel = AParcel_fromJavaParcel(env, data); parcel != nullptr) {
        parcelable = fromParcel<I>(env, parcel);
        AParcel_delete(parcel);
    } else {
        jniThrowExceptionFmt(env, "android/os/BadParcelableException",
                             "Failed to AParcel_fromJavaParcel, for nullptr");
    }
    return parcelable;
}

// Returns a new array of parcelables from given java parcel object.
template <typename I>
std::vector<I> vectorFromJavaParcel(JNIEnv* env, jobject data) {
    int32_t size;
    std::vector<I> result;
    if (AParcel* parcel = AParcel_fromJavaParcel(env, data); parcel != nullptr) {
        if (binder_status_t status = AParcel_readInt32(parcel, &size); status == STATUS_OK) {
            for (int i = 0; i < size; i++) {
                result.push_back(fromParcel<I>(env, parcel));
            }
            AParcel_delete(parcel);
        } else {
            jniThrowExceptionFmt(env, "android/os/BadParcelableException",
                                 "Failed to readInt32 for array length, status %d (%s)", status,
                                 strerror(-status));
        }
    } else {
        jniThrowExceptionFmt(env, "android/os/BadParcelableException",
                             "Failed to AParcel_fromJavaParcel, for nullptr");
    }
    return result;
}

} // namespace android

#endif // _ANDROID_SERVER_VIBRATOR_MANAGER_SERVICE_H
Loading