Loading services/core/java/com/android/server/vibrator/VibratorController.java +8 −35 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import libcore.util.NativeAllocationRegistry; Loading Loading @@ -65,13 +66,9 @@ final class VibratorController { NativeWrapper nativeWrapper) { mNativeWrapper = nativeWrapper; mNativeWrapper.init(vibratorId, listener); // TODO(b/167947076): load supported ones from HAL once API introduced VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping( Float.NaN, nativeWrapper.getResonantFrequency(), Float.NaN, Float.NaN, null); mVibratorInfo = new VibratorInfo(vibratorId, nativeWrapper.getCapabilities(), nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives(), nativeWrapper.getQFactor(), frequencyMapping); mVibratorInfo = mNativeWrapper.getInfo(); Preconditions.checkNotNull(mVibratorInfo, "Failed to retrieve data for vibrator %d", vibratorId); } /** Register state listener for this vibrator. */ Loading Loading @@ -338,19 +335,15 @@ final class VibratorController { private static native long on(long nativePtr, long milliseconds, long vibrationId); private static native void off(long nativePtr); private static native void setAmplitude(long nativePtr, float amplitude); private static native int[] getSupportedEffects(long nativePtr); private static native int[] getSupportedPrimitives(long nativePtr); private static native long performEffect(long nativePtr, long effect, long strength, long vibrationId); private static native long performComposedEffect(long nativePtr, PrimitiveSegment[] effect, long vibrationId); private static native void setExternalControl(long nativePtr, boolean enabled); private static native long getCapabilities(long nativePtr); private static native void alwaysOnEnable(long nativePtr, long id, long effect, long strength); private static native void alwaysOnDisable(long nativePtr, long id); private static native float getResonantFrequency(long nativePtr); private static native float getQFactor(long nativePtr); private static native VibratorInfo getInfo(long nativePtr); private long mNativePtr = 0; Loading Loading @@ -387,16 +380,6 @@ final class VibratorController { setAmplitude(mNativePtr, amplitude); } /** Returns all predefined effects supported by the device vibrator. */ public int[] getSupportedEffects() { return getSupportedEffects(mNativePtr); } /** Returns all compose primitives supported by the device vibrator. */ public int[] getSupportedPrimitives() { return getSupportedPrimitives(mNativePtr); } /** Turns vibrator on to perform one of the supported effects. */ public long perform(long effect, long strength, long vibrationId) { return performEffect(mNativePtr, effect, strength, vibrationId); Loading @@ -412,11 +395,6 @@ final class VibratorController { setExternalControl(mNativePtr, enabled); } /** Returns all capabilities of the device vibrator. */ public long getCapabilities() { return getCapabilities(mNativePtr); } /** Enable always-on vibration with given id and effect. */ public void alwaysOnEnable(long id, long effect, long strength) { alwaysOnEnable(mNativePtr, id, effect, strength); Loading @@ -427,14 +405,9 @@ final class VibratorController { alwaysOnDisable(mNativePtr, id); } /** Gets the vibrator's resonant frequency (F0) */ public float getResonantFrequency() { return getResonantFrequency(mNativePtr); } /** Gets the vibrator's Q factor */ public float getQFactor() { return getQFactor(mNativePtr); /** Return device vibrator metadata. */ public VibratorInfo getInfo() { return getInfo(mNativePtr); } } } services/core/jni/com_android_server_vibrator_VibratorController.cpp +91 −74 Original line number Diff line number Diff line Loading @@ -39,6 +39,10 @@ namespace android { static JavaVM* sJvm = nullptr; static jmethodID sMethodIdOnComplete; static jclass sFrequencyMappingClass; static jmethodID sFrequencyMappingCtor; static jclass sVibratorInfoClass; static jmethodID sVibratorInfoCtor; static struct { jfieldID id; jfieldID scale; Loading Loading @@ -97,7 +101,17 @@ public: jniEnv->DeleteGlobalRef(mCallbackListener); } vibrator::HalController* hal() const { return mHal.get(); } int32_t getVibratorId() const { return mVibratorId; } vibrator::Info getVibratorInfo() { return mHal->getInfo(); } void initHal() { mHal->init(); } template <typename T> vibrator::HalResult<T> halCall(const vibrator::HalFunction<vibrator::HalResult<T>>& fn, const char* functionName) { return mHal->doWithRetry(fn, functionName); } std::function<void()> createCallback(jlong vibrationId) { return [vibrationId, this]() { Loading Loading @@ -133,7 +147,7 @@ static jlong vibratorNativeInit(JNIEnv* env, jclass /* clazz */, jint vibratorId jobject callbackListener) { std::unique_ptr<VibratorControllerWrapper> wrapper = std::make_unique<VibratorControllerWrapper>(env, vibratorId, callbackListener); wrapper->hal()->init(); wrapper->initHal(); return reinterpret_cast<jlong>(wrapper.release()); } Loading @@ -147,7 +161,8 @@ static jboolean vibratorIsAvailable(JNIEnv* env, jclass /* clazz */, jlong ptr) ALOGE("vibratorIsAvailable failed because native wrapper was not initialized"); return JNI_FALSE; } return wrapper->hal()->ping().isOk() ? JNI_TRUE : JNI_FALSE; auto pingFn = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->ping(); }; return wrapper->halCall<void>(pingFn, "ping").isOk() ? JNI_TRUE : JNI_FALSE; } static jlong vibratorOn(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong timeoutMs, Loading @@ -158,7 +173,10 @@ static jlong vibratorOn(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong timeou return -1; } auto callback = wrapper->createCallback(vibrationId); auto result = wrapper->hal()->on(std::chrono::milliseconds(timeoutMs), callback); auto onFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->on(std::chrono::milliseconds(timeoutMs), callback); }; auto result = wrapper->halCall<void>(onFn, "on"); return result.isOk() ? timeoutMs : (result.isUnsupported() ? 0 : -1); } Loading @@ -168,7 +186,8 @@ static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong ptr) { ALOGE("vibratorOff failed because native wrapper was not initialized"); return; } wrapper->hal()->off(); auto offFn = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->off(); }; wrapper->halCall<void>(offFn, "off"); } static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong ptr, jfloat amplitude) { Loading @@ -177,7 +196,10 @@ static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong ptr, jfl ALOGE("vibratorSetAmplitude failed because native wrapper was not initialized"); return; } wrapper->hal()->setAmplitude(static_cast<float>(amplitude)); auto setAmplitudeFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->setAmplitude(static_cast<float>(amplitude)); }; wrapper->halCall<void>(setAmplitudeFn, "setAmplitude"); } static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong ptr, Loading @@ -187,41 +209,10 @@ static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong pt ALOGE("vibratorSetExternalControl failed because native wrapper was not initialized"); return; } wrapper->hal()->setExternalControl(enabled); } static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong ptr) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper == nullptr) { ALOGE("vibratorGetSupportedEffects failed because native wrapper was not initialized"); return nullptr; } auto result = wrapper->hal()->getSupportedEffects(); if (!result.isOk()) { return nullptr; } std::vector<aidl::Effect> supportedEffects = result.value(); jintArray effects = env->NewIntArray(supportedEffects.size()); env->SetIntArrayRegion(effects, 0, supportedEffects.size(), reinterpret_cast<jint*>(supportedEffects.data())); return effects; } static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */, jlong ptr) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper == nullptr) { ALOGE("vibratorGetSupportedPrimitives failed because native wrapper was not initialized"); return nullptr; } auto result = wrapper->hal()->getSupportedPrimitives(); if (!result.isOk()) { return nullptr; } std::vector<aidl::CompositePrimitive> supportedPrimitives = result.value(); jintArray primitives = env->NewIntArray(supportedPrimitives.size()); env->SetIntArrayRegion(primitives, 0, supportedPrimitives.size(), reinterpret_cast<jint*>(supportedPrimitives.data())); return primitives; auto setExternalControlFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->setExternalControl(enabled); }; wrapper->halCall<void>(setExternalControlFn, "setExternalControl"); } static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong effect, Loading @@ -234,7 +225,10 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, j aidl::Effect effectType = static_cast<aidl::Effect>(effect); aidl::EffectStrength effectStrength = static_cast<aidl::EffectStrength>(strength); auto callback = wrapper->createCallback(vibrationId); auto result = wrapper->hal()->performEffect(effectType, effectStrength, callback); auto performEffectFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->performEffect(effectType, effectStrength, callback); }; auto result = wrapper->halCall<std::chrono::milliseconds>(performEffectFn, "performEffect"); return result.isOk() ? result.value().count() : (result.isUnsupported() ? 0 : -1); } Loading @@ -252,20 +246,14 @@ static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlon effects.push_back(effectFromJavaPrimitive(env, element)); } auto callback = wrapper->createCallback(vibrationId); auto result = wrapper->hal()->performComposedEffect(effects, callback); auto performComposedEffectFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->performComposedEffect(effects, callback); }; auto result = wrapper->halCall<std::chrono::milliseconds>(performComposedEffectFn, "performComposedEffect"); return result.isOk() ? result.value().count() : (result.isUnsupported() ? 0 : -1); } static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong ptr) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper == nullptr) { ALOGE("vibratorGetCapabilities failed because native wrapper was not initialized"); return 0; } auto result = wrapper->hal()->getCapabilities(); return result.isOk() ? static_cast<jlong>(result.value()) : 0; } static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong id, jlong effect, jlong strength) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); Loading @@ -273,8 +261,11 @@ static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong ptr, j ALOGE("vibratorAlwaysOnEnable failed because native wrapper was not initialized"); return; } wrapper->hal()->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect), auto alwaysOnEnableFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect), static_cast<aidl::EffectStrength>(strength)); }; wrapper->halCall<void>(alwaysOnEnableFn, "alwaysOnEnable"); } static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong id) { Loading @@ -283,27 +274,48 @@ static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr, ALOGE("vibratorAlwaysOnDisable failed because native wrapper was not initialized"); return; } wrapper->hal()->alwaysOnDisable(static_cast<int32_t>(id)); auto alwaysOnDisableFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->alwaysOnDisable(static_cast<int32_t>(id)); }; wrapper->halCall<void>(alwaysOnDisableFn, "alwaysOnDisable"); } static float vibratorGetResonantFrequency(JNIEnv* env, jclass /* clazz */, jlong ptr) { static jobject vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper == nullptr) { ALOGE("vibratorGetResonantFrequency failed because native wrapper was not initialized"); return NAN; } auto result = wrapper->hal()->getResonantFrequency(); return result.isOk() ? static_cast<jfloat>(result.value()) : NAN; ALOGE("vibratorGetInfo failed because native wrapper was not initialized"); return nullptr; } vibrator::Info info = wrapper->getVibratorInfo(); static float vibratorGetQFactor(JNIEnv* env, jclass /* clazz */, jlong ptr) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper == nullptr) { ALOGE("vibratorGetQFactor failed because native wrapper was not initialized"); return NAN; jlong capabilities = static_cast<jlong>(info.capabilities.valueOr(vibrator::Capabilities::NONE)); jfloat resonantFrequency = static_cast<jfloat>(info.resonantFrequency.valueOr(NAN)); jfloat qFactor = static_cast<jfloat>(info.qFactor.valueOr(NAN)); jintArray supportedEffects = nullptr; jintArray supportedPrimitives = nullptr; if (info.supportedEffects.isOk()) { std::vector<aidl::Effect> effects = info.supportedEffects.value(); supportedEffects = env->NewIntArray(effects.size()); env->SetIntArrayRegion(supportedEffects, 0, effects.size(), reinterpret_cast<jint*>(effects.data())); } auto result = wrapper->hal()->getQFactor(); return result.isOk() ? static_cast<jfloat>(result.value()) : NAN; if (info.supportedPrimitives.isOk()) { std::vector<aidl::CompositePrimitive> primitives = info.supportedPrimitives.value(); supportedPrimitives = env->NewIntArray(primitives.size()); env->SetIntArrayRegion(supportedPrimitives, 0, primitives.size(), reinterpret_cast<jint*>(primitives.data())); } jobject frequencyMapping = env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor, NAN /* minFrequencyHz*/, resonantFrequency, NAN /* frequencyResolutionHz*/, NAN /* suggestedSafeRangeHz */, nullptr /* maxAmplitudes */); return env->NewObject(sVibratorInfoClass, sVibratorInfoCtor, wrapper->getVibratorId(), capabilities, supportedEffects, supportedPrimitives, qFactor, frequencyMapping); } static const JNINativeMethod method_table[] = { Loading @@ -318,14 +330,10 @@ static const JNINativeMethod method_table[] = { {"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect}, {"performComposedEffect", "(J[Landroid/os/vibrator/PrimitiveSegment;J)J", (void*)vibratorPerformComposedEffect}, {"getSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects}, {"getSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives}, {"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl}, {"getCapabilities", "(J)J", (void*)vibratorGetCapabilities}, {"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable}, {"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable}, {"getResonantFrequency", "(J)F", (void*)vibratorGetResonantFrequency}, {"getQFactor", "(J)F", (void*)vibratorGetQFactor}, {"getInfo", "(J)Landroid/os/VibratorInfo;", (void*)vibratorGetInfo}, }; int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) { Loading @@ -340,6 +348,15 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env sPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "mScale", "F"); sPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "mDelay", "I"); jclass frequencyMappingClass = FindClassOrDie(env, "android/os/VibratorInfo$FrequencyMapping"); sFrequencyMappingClass = (jclass)env->NewGlobalRef(frequencyMappingClass); sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFFF[F)V"); jclass vibratorInfoClass = FindClassOrDie(env, "android/os/VibratorInfo"); sVibratorInfoClass = (jclass)env->NewGlobalRef(vibratorInfoClass); sVibratorInfoCtor = GetMethodIDOrDie(env, sVibratorInfoClass, "<init>", "(IJ[I[IFLandroid/os/VibratorInfo$FrequencyMapping;)V"); return jniRegisterNativeMethods(env, "com/android/server/vibrator/VibratorController$NativeWrapper", method_table, NELEM(method_table)); Loading services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java +9 −26 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.os.Handler; import android.os.Looper; import android.os.VibrationEffect; import android.os.VibratorInfo; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.StepSegment; Loading @@ -38,7 +39,6 @@ import java.util.Map; * interactions. */ final class FakeVibratorControllerProvider { private static final int EFFECT_DURATION = 20; private final Map<Long, PrebakedSegment> mEnabledAlwaysOnEffects = new HashMap<>(); Loading Loading @@ -92,26 +92,6 @@ final class FakeVibratorControllerProvider { applyLatency(); } @Override public int[] getSupportedEffects() { return mSupportedEffects; } @Override public int[] getSupportedPrimitives() { return mSupportedPrimitives; } @Override public float getResonantFrequency() { return mResonantFrequency; } @Override public float getQFactor() { return mQFactor; } @Override public long perform(long effect, long strength, long vibrationId) { if (mSupportedEffects == null Loading Loading @@ -140,11 +120,6 @@ final class FakeVibratorControllerProvider { public void setExternalControl(boolean enabled) { } @Override public long getCapabilities() { return mCapabilities; } @Override public void alwaysOnEnable(long id, long effect, long strength) { PrebakedSegment prebaked = new PrebakedSegment((int) effect, false, (int) strength); Loading @@ -156,6 +131,14 @@ final class FakeVibratorControllerProvider { mEnabledAlwaysOnEffects.remove(id); } @Override public VibratorInfo getInfo() { VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping( Float.NaN, mResonantFrequency, Float.NaN, Float.NaN, null); return new VibratorInfo(vibratorId, mCapabilities, mSupportedEffects, mSupportedPrimitives, mQFactor, frequencyMapping); } private void applyLatency() { try { if (mLatency > 0) { Loading services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java +12 −11 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import android.hardware.vibrator.IVibrator; import android.os.IBinder; import android.os.IVibratorStateListener; import android.os.VibrationEffect; import android.os.VibratorInfo; import android.os.test.TestLooper; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; Loading Loading @@ -66,6 +67,7 @@ import org.mockito.junit.MockitoRule; */ @Presubmit public class VibratorControllerTest { private static final int VIBRATOR_ID = 0; @Rule public MockitoRule rule = MockitoJUnit.rule(); @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); Loading @@ -86,23 +88,18 @@ public class VibratorControllerTest { ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); when(mVibratorStateListenerMock.asBinder()).thenReturn(mVibratorStateListenerBinderMock); mockVibratorCapabilities(0); } private VibratorController createController() { return new VibratorController(/* vibratorId= */ 0, mOnCompleteListenerMock, mNativeWrapperMock); } private VibratorController createController(int vibratorId) { return new VibratorController(vibratorId, mOnCompleteListenerMock, mNativeWrapperMock); return new VibratorController(VIBRATOR_ID, mOnCompleteListenerMock, mNativeWrapperMock); } @Test public void createController_initializesNativeWrapper() { int vibratorId = 13; VibratorController controller = createController(vibratorId); assertEquals(vibratorId, controller.getVibratorInfo().getId()); verify(mNativeWrapperMock).init(eq(vibratorId), notNull()); VibratorController controller = createController(); assertEquals(VIBRATOR_ID, controller.getVibratorInfo().getId()); verify(mNativeWrapperMock).init(eq(VIBRATOR_ID), notNull()); } @Test Loading Loading @@ -292,7 +289,11 @@ public class VibratorControllerTest { } private void mockVibratorCapabilities(int capabilities) { when(mNativeWrapperMock.getCapabilities()).thenReturn((long) capabilities); VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping( Float.NaN, Float.NaN, Float.NaN, Float.NaN, null); when(mNativeWrapperMock.getInfo()).thenReturn( new VibratorInfo(VIBRATOR_ID, capabilities, null, null, Float.NaN, frequencyMapping)); } private PrebakedSegment createPrebaked(int effectId, int effectStrength) { Loading Loading
services/core/java/com/android/server/vibrator/VibratorController.java +8 −35 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import libcore.util.NativeAllocationRegistry; Loading Loading @@ -65,13 +66,9 @@ final class VibratorController { NativeWrapper nativeWrapper) { mNativeWrapper = nativeWrapper; mNativeWrapper.init(vibratorId, listener); // TODO(b/167947076): load supported ones from HAL once API introduced VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping( Float.NaN, nativeWrapper.getResonantFrequency(), Float.NaN, Float.NaN, null); mVibratorInfo = new VibratorInfo(vibratorId, nativeWrapper.getCapabilities(), nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives(), nativeWrapper.getQFactor(), frequencyMapping); mVibratorInfo = mNativeWrapper.getInfo(); Preconditions.checkNotNull(mVibratorInfo, "Failed to retrieve data for vibrator %d", vibratorId); } /** Register state listener for this vibrator. */ Loading Loading @@ -338,19 +335,15 @@ final class VibratorController { private static native long on(long nativePtr, long milliseconds, long vibrationId); private static native void off(long nativePtr); private static native void setAmplitude(long nativePtr, float amplitude); private static native int[] getSupportedEffects(long nativePtr); private static native int[] getSupportedPrimitives(long nativePtr); private static native long performEffect(long nativePtr, long effect, long strength, long vibrationId); private static native long performComposedEffect(long nativePtr, PrimitiveSegment[] effect, long vibrationId); private static native void setExternalControl(long nativePtr, boolean enabled); private static native long getCapabilities(long nativePtr); private static native void alwaysOnEnable(long nativePtr, long id, long effect, long strength); private static native void alwaysOnDisable(long nativePtr, long id); private static native float getResonantFrequency(long nativePtr); private static native float getQFactor(long nativePtr); private static native VibratorInfo getInfo(long nativePtr); private long mNativePtr = 0; Loading Loading @@ -387,16 +380,6 @@ final class VibratorController { setAmplitude(mNativePtr, amplitude); } /** Returns all predefined effects supported by the device vibrator. */ public int[] getSupportedEffects() { return getSupportedEffects(mNativePtr); } /** Returns all compose primitives supported by the device vibrator. */ public int[] getSupportedPrimitives() { return getSupportedPrimitives(mNativePtr); } /** Turns vibrator on to perform one of the supported effects. */ public long perform(long effect, long strength, long vibrationId) { return performEffect(mNativePtr, effect, strength, vibrationId); Loading @@ -412,11 +395,6 @@ final class VibratorController { setExternalControl(mNativePtr, enabled); } /** Returns all capabilities of the device vibrator. */ public long getCapabilities() { return getCapabilities(mNativePtr); } /** Enable always-on vibration with given id and effect. */ public void alwaysOnEnable(long id, long effect, long strength) { alwaysOnEnable(mNativePtr, id, effect, strength); Loading @@ -427,14 +405,9 @@ final class VibratorController { alwaysOnDisable(mNativePtr, id); } /** Gets the vibrator's resonant frequency (F0) */ public float getResonantFrequency() { return getResonantFrequency(mNativePtr); } /** Gets the vibrator's Q factor */ public float getQFactor() { return getQFactor(mNativePtr); /** Return device vibrator metadata. */ public VibratorInfo getInfo() { return getInfo(mNativePtr); } } }
services/core/jni/com_android_server_vibrator_VibratorController.cpp +91 −74 Original line number Diff line number Diff line Loading @@ -39,6 +39,10 @@ namespace android { static JavaVM* sJvm = nullptr; static jmethodID sMethodIdOnComplete; static jclass sFrequencyMappingClass; static jmethodID sFrequencyMappingCtor; static jclass sVibratorInfoClass; static jmethodID sVibratorInfoCtor; static struct { jfieldID id; jfieldID scale; Loading Loading @@ -97,7 +101,17 @@ public: jniEnv->DeleteGlobalRef(mCallbackListener); } vibrator::HalController* hal() const { return mHal.get(); } int32_t getVibratorId() const { return mVibratorId; } vibrator::Info getVibratorInfo() { return mHal->getInfo(); } void initHal() { mHal->init(); } template <typename T> vibrator::HalResult<T> halCall(const vibrator::HalFunction<vibrator::HalResult<T>>& fn, const char* functionName) { return mHal->doWithRetry(fn, functionName); } std::function<void()> createCallback(jlong vibrationId) { return [vibrationId, this]() { Loading Loading @@ -133,7 +147,7 @@ static jlong vibratorNativeInit(JNIEnv* env, jclass /* clazz */, jint vibratorId jobject callbackListener) { std::unique_ptr<VibratorControllerWrapper> wrapper = std::make_unique<VibratorControllerWrapper>(env, vibratorId, callbackListener); wrapper->hal()->init(); wrapper->initHal(); return reinterpret_cast<jlong>(wrapper.release()); } Loading @@ -147,7 +161,8 @@ static jboolean vibratorIsAvailable(JNIEnv* env, jclass /* clazz */, jlong ptr) ALOGE("vibratorIsAvailable failed because native wrapper was not initialized"); return JNI_FALSE; } return wrapper->hal()->ping().isOk() ? JNI_TRUE : JNI_FALSE; auto pingFn = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->ping(); }; return wrapper->halCall<void>(pingFn, "ping").isOk() ? JNI_TRUE : JNI_FALSE; } static jlong vibratorOn(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong timeoutMs, Loading @@ -158,7 +173,10 @@ static jlong vibratorOn(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong timeou return -1; } auto callback = wrapper->createCallback(vibrationId); auto result = wrapper->hal()->on(std::chrono::milliseconds(timeoutMs), callback); auto onFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->on(std::chrono::milliseconds(timeoutMs), callback); }; auto result = wrapper->halCall<void>(onFn, "on"); return result.isOk() ? timeoutMs : (result.isUnsupported() ? 0 : -1); } Loading @@ -168,7 +186,8 @@ static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong ptr) { ALOGE("vibratorOff failed because native wrapper was not initialized"); return; } wrapper->hal()->off(); auto offFn = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->off(); }; wrapper->halCall<void>(offFn, "off"); } static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong ptr, jfloat amplitude) { Loading @@ -177,7 +196,10 @@ static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong ptr, jfl ALOGE("vibratorSetAmplitude failed because native wrapper was not initialized"); return; } wrapper->hal()->setAmplitude(static_cast<float>(amplitude)); auto setAmplitudeFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->setAmplitude(static_cast<float>(amplitude)); }; wrapper->halCall<void>(setAmplitudeFn, "setAmplitude"); } static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong ptr, Loading @@ -187,41 +209,10 @@ static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong pt ALOGE("vibratorSetExternalControl failed because native wrapper was not initialized"); return; } wrapper->hal()->setExternalControl(enabled); } static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong ptr) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper == nullptr) { ALOGE("vibratorGetSupportedEffects failed because native wrapper was not initialized"); return nullptr; } auto result = wrapper->hal()->getSupportedEffects(); if (!result.isOk()) { return nullptr; } std::vector<aidl::Effect> supportedEffects = result.value(); jintArray effects = env->NewIntArray(supportedEffects.size()); env->SetIntArrayRegion(effects, 0, supportedEffects.size(), reinterpret_cast<jint*>(supportedEffects.data())); return effects; } static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */, jlong ptr) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper == nullptr) { ALOGE("vibratorGetSupportedPrimitives failed because native wrapper was not initialized"); return nullptr; } auto result = wrapper->hal()->getSupportedPrimitives(); if (!result.isOk()) { return nullptr; } std::vector<aidl::CompositePrimitive> supportedPrimitives = result.value(); jintArray primitives = env->NewIntArray(supportedPrimitives.size()); env->SetIntArrayRegion(primitives, 0, supportedPrimitives.size(), reinterpret_cast<jint*>(supportedPrimitives.data())); return primitives; auto setExternalControlFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->setExternalControl(enabled); }; wrapper->halCall<void>(setExternalControlFn, "setExternalControl"); } static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong effect, Loading @@ -234,7 +225,10 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, j aidl::Effect effectType = static_cast<aidl::Effect>(effect); aidl::EffectStrength effectStrength = static_cast<aidl::EffectStrength>(strength); auto callback = wrapper->createCallback(vibrationId); auto result = wrapper->hal()->performEffect(effectType, effectStrength, callback); auto performEffectFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->performEffect(effectType, effectStrength, callback); }; auto result = wrapper->halCall<std::chrono::milliseconds>(performEffectFn, "performEffect"); return result.isOk() ? result.value().count() : (result.isUnsupported() ? 0 : -1); } Loading @@ -252,20 +246,14 @@ static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlon effects.push_back(effectFromJavaPrimitive(env, element)); } auto callback = wrapper->createCallback(vibrationId); auto result = wrapper->hal()->performComposedEffect(effects, callback); auto performComposedEffectFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->performComposedEffect(effects, callback); }; auto result = wrapper->halCall<std::chrono::milliseconds>(performComposedEffectFn, "performComposedEffect"); return result.isOk() ? result.value().count() : (result.isUnsupported() ? 0 : -1); } static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong ptr) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper == nullptr) { ALOGE("vibratorGetCapabilities failed because native wrapper was not initialized"); return 0; } auto result = wrapper->hal()->getCapabilities(); return result.isOk() ? static_cast<jlong>(result.value()) : 0; } static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong id, jlong effect, jlong strength) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); Loading @@ -273,8 +261,11 @@ static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong ptr, j ALOGE("vibratorAlwaysOnEnable failed because native wrapper was not initialized"); return; } wrapper->hal()->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect), auto alwaysOnEnableFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect), static_cast<aidl::EffectStrength>(strength)); }; wrapper->halCall<void>(alwaysOnEnableFn, "alwaysOnEnable"); } static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong id) { Loading @@ -283,27 +274,48 @@ static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr, ALOGE("vibratorAlwaysOnDisable failed because native wrapper was not initialized"); return; } wrapper->hal()->alwaysOnDisable(static_cast<int32_t>(id)); auto alwaysOnDisableFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->alwaysOnDisable(static_cast<int32_t>(id)); }; wrapper->halCall<void>(alwaysOnDisableFn, "alwaysOnDisable"); } static float vibratorGetResonantFrequency(JNIEnv* env, jclass /* clazz */, jlong ptr) { static jobject vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper == nullptr) { ALOGE("vibratorGetResonantFrequency failed because native wrapper was not initialized"); return NAN; } auto result = wrapper->hal()->getResonantFrequency(); return result.isOk() ? static_cast<jfloat>(result.value()) : NAN; ALOGE("vibratorGetInfo failed because native wrapper was not initialized"); return nullptr; } vibrator::Info info = wrapper->getVibratorInfo(); static float vibratorGetQFactor(JNIEnv* env, jclass /* clazz */, jlong ptr) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper == nullptr) { ALOGE("vibratorGetQFactor failed because native wrapper was not initialized"); return NAN; jlong capabilities = static_cast<jlong>(info.capabilities.valueOr(vibrator::Capabilities::NONE)); jfloat resonantFrequency = static_cast<jfloat>(info.resonantFrequency.valueOr(NAN)); jfloat qFactor = static_cast<jfloat>(info.qFactor.valueOr(NAN)); jintArray supportedEffects = nullptr; jintArray supportedPrimitives = nullptr; if (info.supportedEffects.isOk()) { std::vector<aidl::Effect> effects = info.supportedEffects.value(); supportedEffects = env->NewIntArray(effects.size()); env->SetIntArrayRegion(supportedEffects, 0, effects.size(), reinterpret_cast<jint*>(effects.data())); } auto result = wrapper->hal()->getQFactor(); return result.isOk() ? static_cast<jfloat>(result.value()) : NAN; if (info.supportedPrimitives.isOk()) { std::vector<aidl::CompositePrimitive> primitives = info.supportedPrimitives.value(); supportedPrimitives = env->NewIntArray(primitives.size()); env->SetIntArrayRegion(supportedPrimitives, 0, primitives.size(), reinterpret_cast<jint*>(primitives.data())); } jobject frequencyMapping = env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor, NAN /* minFrequencyHz*/, resonantFrequency, NAN /* frequencyResolutionHz*/, NAN /* suggestedSafeRangeHz */, nullptr /* maxAmplitudes */); return env->NewObject(sVibratorInfoClass, sVibratorInfoCtor, wrapper->getVibratorId(), capabilities, supportedEffects, supportedPrimitives, qFactor, frequencyMapping); } static const JNINativeMethod method_table[] = { Loading @@ -318,14 +330,10 @@ static const JNINativeMethod method_table[] = { {"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect}, {"performComposedEffect", "(J[Landroid/os/vibrator/PrimitiveSegment;J)J", (void*)vibratorPerformComposedEffect}, {"getSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects}, {"getSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives}, {"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl}, {"getCapabilities", "(J)J", (void*)vibratorGetCapabilities}, {"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable}, {"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable}, {"getResonantFrequency", "(J)F", (void*)vibratorGetResonantFrequency}, {"getQFactor", "(J)F", (void*)vibratorGetQFactor}, {"getInfo", "(J)Landroid/os/VibratorInfo;", (void*)vibratorGetInfo}, }; int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) { Loading @@ -340,6 +348,15 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env sPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "mScale", "F"); sPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "mDelay", "I"); jclass frequencyMappingClass = FindClassOrDie(env, "android/os/VibratorInfo$FrequencyMapping"); sFrequencyMappingClass = (jclass)env->NewGlobalRef(frequencyMappingClass); sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFFF[F)V"); jclass vibratorInfoClass = FindClassOrDie(env, "android/os/VibratorInfo"); sVibratorInfoClass = (jclass)env->NewGlobalRef(vibratorInfoClass); sVibratorInfoCtor = GetMethodIDOrDie(env, sVibratorInfoClass, "<init>", "(IJ[I[IFLandroid/os/VibratorInfo$FrequencyMapping;)V"); return jniRegisterNativeMethods(env, "com/android/server/vibrator/VibratorController$NativeWrapper", method_table, NELEM(method_table)); Loading
services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java +9 −26 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.os.Handler; import android.os.Looper; import android.os.VibrationEffect; import android.os.VibratorInfo; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.StepSegment; Loading @@ -38,7 +39,6 @@ import java.util.Map; * interactions. */ final class FakeVibratorControllerProvider { private static final int EFFECT_DURATION = 20; private final Map<Long, PrebakedSegment> mEnabledAlwaysOnEffects = new HashMap<>(); Loading Loading @@ -92,26 +92,6 @@ final class FakeVibratorControllerProvider { applyLatency(); } @Override public int[] getSupportedEffects() { return mSupportedEffects; } @Override public int[] getSupportedPrimitives() { return mSupportedPrimitives; } @Override public float getResonantFrequency() { return mResonantFrequency; } @Override public float getQFactor() { return mQFactor; } @Override public long perform(long effect, long strength, long vibrationId) { if (mSupportedEffects == null Loading Loading @@ -140,11 +120,6 @@ final class FakeVibratorControllerProvider { public void setExternalControl(boolean enabled) { } @Override public long getCapabilities() { return mCapabilities; } @Override public void alwaysOnEnable(long id, long effect, long strength) { PrebakedSegment prebaked = new PrebakedSegment((int) effect, false, (int) strength); Loading @@ -156,6 +131,14 @@ final class FakeVibratorControllerProvider { mEnabledAlwaysOnEffects.remove(id); } @Override public VibratorInfo getInfo() { VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping( Float.NaN, mResonantFrequency, Float.NaN, Float.NaN, null); return new VibratorInfo(vibratorId, mCapabilities, mSupportedEffects, mSupportedPrimitives, mQFactor, frequencyMapping); } private void applyLatency() { try { if (mLatency > 0) { Loading
services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java +12 −11 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import android.hardware.vibrator.IVibrator; import android.os.IBinder; import android.os.IVibratorStateListener; import android.os.VibrationEffect; import android.os.VibratorInfo; import android.os.test.TestLooper; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; Loading Loading @@ -66,6 +67,7 @@ import org.mockito.junit.MockitoRule; */ @Presubmit public class VibratorControllerTest { private static final int VIBRATOR_ID = 0; @Rule public MockitoRule rule = MockitoJUnit.rule(); @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); Loading @@ -86,23 +88,18 @@ public class VibratorControllerTest { ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); when(mVibratorStateListenerMock.asBinder()).thenReturn(mVibratorStateListenerBinderMock); mockVibratorCapabilities(0); } private VibratorController createController() { return new VibratorController(/* vibratorId= */ 0, mOnCompleteListenerMock, mNativeWrapperMock); } private VibratorController createController(int vibratorId) { return new VibratorController(vibratorId, mOnCompleteListenerMock, mNativeWrapperMock); return new VibratorController(VIBRATOR_ID, mOnCompleteListenerMock, mNativeWrapperMock); } @Test public void createController_initializesNativeWrapper() { int vibratorId = 13; VibratorController controller = createController(vibratorId); assertEquals(vibratorId, controller.getVibratorInfo().getId()); verify(mNativeWrapperMock).init(eq(vibratorId), notNull()); VibratorController controller = createController(); assertEquals(VIBRATOR_ID, controller.getVibratorInfo().getId()); verify(mNativeWrapperMock).init(eq(VIBRATOR_ID), notNull()); } @Test Loading Loading @@ -292,7 +289,11 @@ public class VibratorControllerTest { } private void mockVibratorCapabilities(int capabilities) { when(mNativeWrapperMock.getCapabilities()).thenReturn((long) capabilities); VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping( Float.NaN, Float.NaN, Float.NaN, Float.NaN, null); when(mNativeWrapperMock.getInfo()).thenReturn( new VibratorInfo(VIBRATOR_ID, capabilities, null, null, Float.NaN, frequencyMapping)); } private PrebakedSegment createPrebaked(int effectId, int effectStrength) { Loading