Loading services/core/java/com/android/server/vibrator/HalNativeHandler.java +63 −2 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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); } services/core/java/com/android/server/vibrator/VibratorManagerService.java +100 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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. */ Loading services/core/java/com/android/server/vibrator/VintfHalVibrator.java +162 −16 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. */ Loading Loading @@ -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. Loading @@ -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); Loading @@ -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); } Loading @@ -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); } Loading @@ -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); } Loading @@ -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); } Loading @@ -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); } Loading Loading @@ -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); } Loading services/core/jni/com_android_server_vibrator_VibratorManagerService.cpp +127 −16 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/jni/com_android_server_vibrator_VibratorManagerService.h +51 −0 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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
services/core/java/com/android/server/vibrator/HalNativeHandler.java +63 −2 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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); }
services/core/java/com/android/server/vibrator/VibratorManagerService.java +100 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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. */ Loading
services/core/java/com/android/server/vibrator/VintfHalVibrator.java +162 −16 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. */ Loading Loading @@ -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. Loading @@ -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); Loading @@ -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); } Loading @@ -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); } Loading @@ -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); } Loading @@ -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); } Loading @@ -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); } Loading Loading @@ -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); } Loading
services/core/jni/com_android_server_vibrator_VibratorManagerService.cpp +127 −16 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/jni/com_android_server_vibrator_VibratorManagerService.h +51 −0 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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