From ce89efbed15c7b028ba9f3c4edd71a18b1f41d4a Mon Sep 17 00:00:00 2001 From: "Harpreet \\\"Eli\\\" Sangha" Date: Mon, 2 Sep 2019 14:21:49 +0900 Subject: [PATCH 1/2] Vibrator Service: Allow HAL Version Checking Broke up the halCall() API into two components, one that implements the initial service retrieval and one that implements the retry on connection loss. This allows the service retrival API to double as test for supported version with little overhead. Test: Manually via CLI Change-Id: I12cf8838e933187d473157c9576d1b13b03913d4 Signed-off-by: Harpreet \"Eli\" Sangha (cherry picked from commit e0b7951d632a5334f17b7e16cdf4bc4cfe9b2196) Merged-In: I12cf8838e933187d473157c9576d1b13b03913d4 --- .../com_android_server_VibratorService.cpp | 78 ++++++++++++------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp index c59e6056b72e0..e77c027b60700 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -56,37 +56,57 @@ inline Return NullptrStatus() { return Return{Status::fromExceptionCode(Status::EX_NULL_POINTER)}; } -// Helper used to transparently deal with the vibrator HAL becoming unavailable. +template +class HalWrapper { + public: + static std::unique_ptr Create() { + // Assume that if getService returns a nullptr, HAL is not available on the + // device. + auto hal = I::getService(); + return hal ? std::unique_ptr(new HalWrapper(std::move(hal))) : nullptr; + } + + // Helper used to transparently deal with the vibrator HAL becoming unavailable. + template + Return call(Return (I::* fn)(Args0...), Args1&&... args1) { + // Return doesn't have a default constructor, so make a Return with + // STATUS::EX_NONE. + using ::android::hardware::Status; + Return ret{Status::fromExceptionCode(Status::EX_NONE)}; + + // Note that ret is guaranteed to be changed after this loop. + for (int i = 0; i < NUM_TRIES; ++i) { + ret = (mHal == nullptr) ? NullptrStatus() + : (*mHal.*fn)(std::forward(args1)...); + + if (ret.isOk()) { + break; + } + + ALOGE("Failed to issue command to vibrator HAL. Retrying."); + // Restoring connection to the HAL. + mHal = I::tryGetService(); + } + return ret; + } + + private: + HalWrapper(sp &&hal) : mHal(std::move(hal)) {} + + private: + sp mHal; +}; + +template +static auto getHal() { + static auto sHalWrapper = HalWrapper::Create(); + return sHalWrapper.get(); +} + template Return halCall(Return (I::* fn)(Args0...), Args1&&... args1) { - // Assume that if getService returns a nullptr, HAL is not available on the - // device. - static sp sHal = I::getService(); - static bool sAvailable = sHal != nullptr; - - if (!sAvailable) { - return NullptrStatus(); - } - - // Return doesn't have a default constructor, so make a Return with - // STATUS::EX_NONE. - using ::android::hardware::Status; - Return ret{Status::fromExceptionCode(Status::EX_NONE)}; - - // Note that ret is guaranteed to be changed after this loop. - for (int i = 0; i < NUM_TRIES; ++i) { - ret = (sHal == nullptr) ? NullptrStatus() - : (*sHal.*fn)(std::forward(args1)...); - - if (ret.isOk()) { - break; - } - - ALOGE("Failed to issue command to vibrator HAL. Retrying."); - // Restoring connection to the HAL. - sHal = I::tryGetService(); - } - return ret; + auto hal = getHal(); + return hal ? hal->call(fn, std::forward(args1)...) : NullptrStatus(); } template From 89ffc2d327ce06ed39f2f219794eca22168a7bda Mon Sep 17 00:00:00 2001 From: "Harpreet \"Eli\" Sangha" Date: Thu, 25 Jul 2019 11:32:57 +0900 Subject: [PATCH 2/2] Vibrator Service: Support Async Callback APIs Bug: 136220871 Test: TBD Change-Id: Ia639dd01081218db0bc3029316a58a03b9d4d927 (cherry picked from commit 7a4f1d23eaa5c55d84d8316bb531f7be03b08efb) Merged-In: Ia639dd01081218db0bc3029316a58a03b9d4d927 --- services/core/Android.bp | 2 +- .../com/android/server/VibratorService.java | 27 ++++++-- services/core/jni/Android.bp | 1 + .../com_android_server_VibratorService.cpp | 69 ++++++++++++++----- 4 files changed, 76 insertions(+), 23 deletions(-) diff --git a/services/core/Android.bp b/services/core/Android.bp index fcf012cd3a6bc..fc0e286fc9934 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -49,7 +49,7 @@ java_library_static { "android.hardware.biometrics.fingerprint-V2.1-java", "android.hardware.oemlock-V1.0-java", "android.hardware.tetheroffload.control-V1.0-java", - "android.hardware.vibrator-V1.0-java", + "android.hardware.vibrator-V1.4-java", "android.hardware.configstore-V1.0-java", "android.hardware.contexthub-V1.0-java", "android.hidl.manager-V1.2-java", diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 9936d73fb800c..d622fb433ed85 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -29,6 +29,7 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.input.InputManager; import android.hardware.vibrator.V1_0.EffectStrength; +import android.hardware.vibrator.V1_4.Capabilities; import android.icu.text.DateFormat; import android.media.AudioAttributes; import android.media.AudioManager; @@ -108,6 +109,9 @@ public class VibratorService extends IVibratorService.Stub // If a vibration is playing for longer than 5s, it's probably not haptic feedback. private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000; + // If HAL supports callbacks set the timeout to ASYNC_TIMEOUT_MULTIPLIER * duration. + private static final long ASYNC_TIMEOUT_MULTIPLIER = 2; + // A mapping from the intensity adjustment to the scaling to apply, where the intensity // adjustment is defined as the delta between the default intensity level and the user selected @@ -123,6 +127,7 @@ public class VibratorService extends IVibratorService.Stub private final boolean mAllowPriorityVibrationsInLowPowerMode; private final boolean mSupportsAmplitudeControl; private final boolean mSupportsExternalControl; + private final long mCapabilities; private final int mDefaultVibrationAmplitude; private final SparseArray mFallbackEffects; private final SparseArray mProcStatesCache = new SparseArray(); @@ -163,9 +168,10 @@ public class VibratorService extends IVibratorService.Stub static native void vibratorOff(); static native boolean vibratorSupportsAmplitudeControl(); static native void vibratorSetAmplitude(int amplitude); - static native long vibratorPerformEffect(long effect, long strength); + static native long vibratorPerformEffect(long effect, long strength, Vibration vibration); static native boolean vibratorSupportsExternalControl(); static native void vibratorSetExternalControl(boolean enabled); + static native long vibratorGetCapabilities(); private final IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) { @@ -226,6 +232,14 @@ public class VibratorService extends IVibratorService.Stub } } + private void onComplete() { + synchronized (mLock) { + if (this == mCurrentVibration) { + doCancelVibrateLocked(); + } + } + } + public boolean hasTimeoutLongerThan(long millis) { final long duration = effect.getDuration(); return duration >= 0 && duration > millis; @@ -347,6 +361,7 @@ public class VibratorService extends IVibratorService.Stub mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl(); mSupportsExternalControl = vibratorSupportsExternalControl(); + mCapabilities = vibratorGetCapabilities(); mContext = context; PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); @@ -1135,10 +1150,14 @@ public class VibratorService extends IVibratorService.Stub } // Input devices don't support prebaked effect, so skip trying it with them. if (!usingInputDeviceVibrators) { - long timeout = vibratorPerformEffect(prebaked.getId(), - prebaked.getEffectStrength()); + long duration = vibratorPerformEffect(prebaked.getId(), + prebaked.getEffectStrength(), vib); + long timeout = duration; + if ((mCapabilities & Capabilities.PERFORM_COMPLETION_CALLBACK) != 0) { + timeout *= ASYNC_TIMEOUT_MULTIPLIER; + } if (timeout > 0) { - noteVibratorOnLocked(vib.uid, timeout); + noteVibratorOnLocked(vib.uid, duration); return timeout; } } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 3bc239b535291..248baf7913356 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -130,6 +130,7 @@ cc_defaults { "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", "android.hardware.vibrator@1.3", + "android.hardware.vibrator@1.4", "android.hardware.vr@1.0", "android.frameworks.schedulerservice@1.0", "android.frameworks.sensorservice@1.0", diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp index e77c027b60700..64c7935efff93 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -16,17 +16,12 @@ #define LOG_TAG "VibratorService" -#include -#include -#include -#include -#include -#include -#include +#include #include "jni.h" #include #include "android_runtime/AndroidRuntime.h" +#include "core_jni_helpers.h" #include #include @@ -36,6 +31,7 @@ #include using android::hardware::Return; +using android::hardware::Void; using android::hardware::vibrator::V1_0::EffectStrength; using android::hardware::vibrator::V1_0::Status; using android::hardware::vibrator::V1_1::Effect_1_1; @@ -44,9 +40,32 @@ namespace V1_0 = android::hardware::vibrator::V1_0; namespace V1_1 = android::hardware::vibrator::V1_1; namespace V1_2 = android::hardware::vibrator::V1_2; namespace V1_3 = android::hardware::vibrator::V1_3; +namespace V1_4 = android::hardware::vibrator::V1_4; namespace android { +static jmethodID sMethodIdOnComplete; + +class VibratorCallback : public V1_4::IVibratorCallback { + public: + VibratorCallback(JNIEnv *env, jobject vibration) : + mVibration(MakeGlobalRefOrDie(env, vibration)) {} + + ~VibratorCallback() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + env->DeleteGlobalRef(mVibration); + } + + Return onComplete() override { + auto env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mVibration, sMethodIdOnComplete); + return Void(); + } + + private: + jobject mVibration; +}; + static constexpr int NUM_TRIES = 2; // Creates a Return with STATUS::EX_NULL_POINTER. @@ -119,17 +138,17 @@ bool isValidEffect(jlong effect) { return val >= *iter.begin() && val <= *std::prev(iter.end()); } -static void vibratorInit(JNIEnv /* env */, jobject /* clazz */) +static void vibratorInit(JNIEnv *env, jclass clazz) { halCall(&V1_0::IVibrator::ping).isOk(); } -static jboolean vibratorExists(JNIEnv* /* env */, jobject /* clazz */) +static jboolean vibratorExists(JNIEnv* /* env */, jclass /* clazz */) { return halCall(&V1_0::IVibrator::ping).isOk() ? JNI_TRUE : JNI_FALSE; } -static void vibratorOn(JNIEnv* /* env */, jobject /* clazz */, jlong timeout_ms) +static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms) { Status retStatus = halCall(&V1_0::IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR); if (retStatus != Status::OK) { @@ -137,7 +156,7 @@ static void vibratorOn(JNIEnv* /* env */, jobject /* clazz */, jlong timeout_ms) } } -static void vibratorOff(JNIEnv* /* env */, jobject /* clazz */) +static void vibratorOff(JNIEnv* /* env */, jclass /* clazz */) { Status retStatus = halCall(&V1_0::IVibrator::off).withDefault(Status::UNKNOWN_ERROR); if (retStatus != Status::OK) { @@ -145,11 +164,11 @@ static void vibratorOff(JNIEnv* /* env */, jobject /* clazz */) } } -static jlong vibratorSupportsAmplitudeControl(JNIEnv*, jobject) { +static jlong vibratorSupportsAmplitudeControl(JNIEnv*, jclass) { return halCall(&V1_0::IVibrator::supportsAmplitudeControl).withDefault(false); } -static void vibratorSetAmplitude(JNIEnv*, jobject, jint amplitude) { +static void vibratorSetAmplitude(JNIEnv*, jclass, jint amplitude) { Status status = halCall(&V1_0::IVibrator::setAmplitude, static_cast(amplitude)) .withDefault(Status::UNKNOWN_ERROR); if (status != Status::OK) { @@ -158,11 +177,11 @@ static void vibratorSetAmplitude(JNIEnv*, jobject, jint amplitude) { } } -static jboolean vibratorSupportsExternalControl(JNIEnv*, jobject) { +static jboolean vibratorSupportsExternalControl(JNIEnv*, jclass) { return halCall(&V1_3::IVibrator::supportsExternalControl).withDefault(false); } -static void vibratorSetExternalControl(JNIEnv*, jobject, jboolean enabled) { +static void vibratorSetExternalControl(JNIEnv*, jclass, jboolean enabled) { Status status = halCall(&V1_3::IVibrator::setExternalControl, static_cast(enabled)) .withDefault(Status::UNKNOWN_ERROR); if (status != Status::OK) { @@ -171,7 +190,8 @@ static void vibratorSetExternalControl(JNIEnv*, jobject, jboolean enabled) { } } -static jlong vibratorPerformEffect(JNIEnv*, jobject, jlong effect, jlong strength) { +static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong strength, + jobject vibration) { Status status; uint32_t lengthMs; auto callback = [&status, &lengthMs](Status retStatus, uint32_t retLengthMs) { @@ -181,7 +201,11 @@ static jlong vibratorPerformEffect(JNIEnv*, jobject, jlong effect, jlong strengt EffectStrength effectStrength(static_cast(strength)); Return ret; - if (isValidEffect(effect)) { + if (auto hal = getHal(); hal && isValidEffect(effect)) { + sp effectCallback = new VibratorCallback(env, vibration); + ret = hal->call(&V1_4::IVibrator::perform_1_4, static_cast(effect), + effectStrength, effectCallback, callback); + } else if (isValidEffect(effect)) { ret = halCall(&V1_0::IVibrator::perform, static_cast(effect), effectStrength, callback); } else if (isValidEffect(effect)) { @@ -218,6 +242,10 @@ static jlong vibratorPerformEffect(JNIEnv*, jobject, jlong effect, jlong strengt return -1; } +static jlong vibratorGetCapabilities(JNIEnv*, jclass) { + return halCall(&V1_4::IVibrator::getCapabilities).withDefault(0); +} + static const JNINativeMethod method_table[] = { { "vibratorExists", "()Z", (void*)vibratorExists }, { "vibratorInit", "()V", (void*)vibratorInit }, @@ -225,13 +253,18 @@ static const JNINativeMethod method_table[] = { { "vibratorOff", "()V", (void*)vibratorOff }, { "vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl}, { "vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude}, - { "vibratorPerformEffect", "(JJ)J", (void*)vibratorPerformEffect}, + { "vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;)J", + (void*)vibratorPerformEffect}, { "vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl}, { "vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl}, + { "vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities}, }; int register_android_server_VibratorService(JNIEnv *env) { + sMethodIdOnComplete = GetMethodIDOrDie(env, + FindClassOrDie(env, "com/android/server/VibratorService$Vibration"), + "onComplete", "()V"); return jniRegisterNativeMethods(env, "com/android/server/VibratorService", method_table, NELEM(method_table)); }