Merge "Use vibration id as callback reference in VibratorService"

This commit is contained in:
TreeHugger Robot
2020-08-24 16:33:13 +00:00
committed by Android (Google) Code Review
3 changed files with 227 additions and 232 deletions

View File

@@ -85,6 +85,7 @@ import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class VibratorService extends IVibratorService.Stub
implements InputManager.InputDeviceListener {
@@ -114,6 +115,9 @@ public class VibratorService extends IVibratorService.Stub
private static final VibrationAttributes DEFAULT_ATTRIBUTES =
new VibrationAttributes.Builder().build();
// Used to generate globally unique vibration ids.
private final AtomicInteger mNextVibrationId = new AtomicInteger(1); // 0 = no callback
// 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
// intensity level. It's important that we apply the scaling on the delta between the two so
@@ -171,34 +175,34 @@ public class VibratorService extends IVibratorService.Stub
private int mRingIntensity;
private SparseArray<Vibration> mAlwaysOnEffects = new SparseArray<>();
static native long vibratorInit();
static native long vibratorInit(OnCompleteListener listener);
static native long vibratorGetFinalizer();
static native boolean vibratorExists(long controllerPtr);
static native boolean vibratorExists(long nativeServicePtr);
static native void vibratorOn(long controllerPtr, long milliseconds, Vibration vibration);
static native void vibratorOn(long nativeServicePtr, long milliseconds, long vibrationId);
static native void vibratorOff(long controllerPtr);
static native void vibratorOff(long nativeServicePtr);
static native void vibratorSetAmplitude(long controllerPtr, int amplitude);
static native void vibratorSetAmplitude(long nativeServicePtr, int amplitude);
static native int[] vibratorGetSupportedEffects(long controllerPtr);
static native int[] vibratorGetSupportedEffects(long nativeServicePtr);
static native int[] vibratorGetSupportedPrimitives(long controllerPtr);
static native int[] vibratorGetSupportedPrimitives(long nativeServicePtr);
static native long vibratorPerformEffect(
long controllerPtr, long effect, long strength, Vibration vibration);
long nativeServicePtr, long effect, long strength, long vibrationId);
static native void vibratorPerformComposedEffect(long controllerPtr,
VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration);
static native void vibratorPerformComposedEffect(long nativeServicePtr,
VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId);
static native void vibratorSetExternalControl(long controllerPtr, boolean enabled);
static native void vibratorSetExternalControl(long nativeServicePtr, boolean enabled);
static native long vibratorGetCapabilities(long controllerPtr);
static native void vibratorAlwaysOnEnable(long controllerPtr, long id, long effect,
static native long vibratorGetCapabilities(long nativeServicePtr);
static native void vibratorAlwaysOnEnable(long nativeServicePtr, long id, long effect,
long strength);
static native void vibratorAlwaysOnDisable(long controllerPtr, long id);
static native void vibratorAlwaysOnDisable(long nativeServicePtr, long id);
private final IUidObserver mUidObserver = new IUidObserver.Stub() {
@Override public void onUidStateChanged(int uid, int procState, long procStateSeq,
@@ -220,12 +224,19 @@ public class VibratorService extends IVibratorService.Stub
}
};
/** Listener for vibration completion callbacks from native. */
public interface OnCompleteListener {
/** Callback triggered when vibration is complete, identified by {@link Vibration#id}. */
void onComplete(long vibrationId);
}
/**
* Holder for a vibration to be played. This class can be shared with native methods for
* hardware callback support.
*/
@VisibleForTesting
public final class Vibration implements IBinder.DeathRecipient {
private final class Vibration implements IBinder.DeathRecipient {
public final IBinder token;
// Start time in CLOCK_BOOTTIME base.
public final long startTime;
@@ -234,6 +245,7 @@ public class VibratorService extends IVibratorService.Stub
// not to be affected by discontinuities created by RTC adjustments.
public final long startTimeDebug;
public final VibrationAttributes attrs;
public final long id;
public final int uid;
public final String opPkg;
public final String reason;
@@ -248,6 +260,7 @@ public class VibratorService extends IVibratorService.Stub
VibrationAttributes attrs, int uid, String opPkg, String reason) {
this.token = token;
this.effect = effect;
this.id = mNextVibrationId.getAndIncrement();
this.startTime = SystemClock.elapsedRealtime();
this.startTimeDebug = System.currentTimeMillis();
this.attrs = attrs;
@@ -268,19 +281,6 @@ public class VibratorService extends IVibratorService.Stub
}
}
/** Callback for when vibration is complete, to be called by native. */
@VisibleForTesting
public void onComplete() {
synchronized (mLock) {
if (this == mCurrentVibration) {
if (DEBUG) {
Slog.d(TAG, "Vibration finished by callback, cleaning up");
}
doCancelVibrateLocked();
}
}
}
public boolean hasTimeoutLongerThan(long millis) {
final long duration = effect.getDuration();
return duration >= 0 && duration > millis;
@@ -385,14 +385,14 @@ public class VibratorService extends IVibratorService.Stub
mNativeWrapper = injector.getNativeWrapper();
mH = injector.createHandler(Looper.myLooper());
long controllerPtr = mNativeWrapper.vibratorInit();
long nativeServicePtr = mNativeWrapper.vibratorInit(this::onVibrationComplete);
long finalizerPtr = mNativeWrapper.vibratorGetFinalizer();
if (finalizerPtr != 0) {
NativeAllocationRegistry registry =
NativeAllocationRegistry.createMalloced(
VibratorService.class.getClassLoader(), finalizerPtr);
registry.registerNativeAllocation(this, controllerPtr);
registry.registerNativeAllocation(this, nativeServicePtr);
}
// Reset the hardware to a default state, in case this is a runtime
@@ -549,6 +549,19 @@ public class VibratorService extends IVibratorService.Stub
}
}
/** Callback for when vibration is complete, to be called by native. */
@VisibleForTesting
public void onVibrationComplete(long vibrationId) {
synchronized (mLock) {
if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) {
if (DEBUG) {
Slog.d(TAG, "Vibration finished by callback, cleaning up");
}
doCancelVibrateLocked();
}
}
}
@Override // Binder call
public boolean hasVibrator() {
return doVibratorExists();
@@ -1266,18 +1279,18 @@ public class VibratorService extends IVibratorService.Stub
return mNativeWrapper.vibratorExists();
}
/** Vibrates with native callback trigger for {@link Vibration#onComplete()}. */
/** Vibrates with native callback trigger for {@link #onVibrationComplete(long)}. */
private void doVibratorOn(long millis, int amplitude, Vibration vib) {
doVibratorOn(millis, amplitude, vib.uid, vib.attrs, vib);
doVibratorOn(millis, amplitude, vib.uid, vib.attrs, vib.id);
}
/** Vibrates without native callback. */
private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) {
doVibratorOn(millis, amplitude, uid, attrs, /* vib= */ null);
doVibratorOn(millis, amplitude, uid, attrs, /* vibrationId= */ 0);
}
private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs,
@Nullable Vibration vib) {
long vibrationId) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
try {
synchronized (mInputDeviceVibrators) {
@@ -1299,7 +1312,7 @@ public class VibratorService extends IVibratorService.Stub
// Note: ordering is important here! Many haptic drivers will reset their
// amplitude when enabled, so we always have to enable first, then set the
// amplitude.
mNativeWrapper.vibratorOn(millis, vib);
mNativeWrapper.vibratorOn(millis, vibrationId);
doVibratorSetAmplitude(amplitude);
}
}
@@ -1348,7 +1361,7 @@ public class VibratorService extends IVibratorService.Stub
// Input devices don't support prebaked effect, so skip trying it with them.
if (!usingInputDeviceVibrators) {
long duration = mNativeWrapper.vibratorPerformEffect(
prebaked.getId(), prebaked.getEffectStrength(), vib);
prebaked.getId(), prebaked.getEffectStrength(), vib.id);
if (duration > 0) {
noteVibratorOnLocked(vib.uid, duration);
return;
@@ -1395,7 +1408,7 @@ public class VibratorService extends IVibratorService.Stub
PrimitiveEffect[] primitiveEffects =
composed.getPrimitiveEffects().toArray(new PrimitiveEffect[0]);
mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib);
mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib.id);
// Composed effects don't actually give us an estimated duration, so we just guess here.
noteVibratorOnLocked(vib.uid, 10 * primitiveEffects.length);
@@ -1726,20 +1739,20 @@ public class VibratorService extends IVibratorService.Stub
@VisibleForTesting
public static class NativeWrapper {
private long mNativeControllerPtr = 0;
private long mNativeServicePtr = 0;
/** Checks if vibrator exists on device. */
public boolean vibratorExists() {
return VibratorService.vibratorExists(mNativeControllerPtr);
return VibratorService.vibratorExists(mNativeServicePtr);
}
/**
* Returns native pointer to newly created controller and initializes connection to vibrator
* HAL service.
*/
public long vibratorInit() {
mNativeControllerPtr = VibratorService.vibratorInit();
return mNativeControllerPtr;
public long vibratorInit(OnCompleteListener listener) {
mNativeServicePtr = VibratorService.vibratorInit(listener);
return mNativeServicePtr;
}
/** Returns pointer to native finalizer function to be called by GC. */
@@ -1748,60 +1761,61 @@ public class VibratorService extends IVibratorService.Stub
}
/** Turns vibrator on for given time. */
public void vibratorOn(long milliseconds, @Nullable Vibration vibration) {
VibratorService.vibratorOn(mNativeControllerPtr, milliseconds, vibration);
public void vibratorOn(long milliseconds, long vibrationId) {
VibratorService.vibratorOn(mNativeServicePtr, milliseconds, vibrationId);
}
/** Turns vibrator off. */
public void vibratorOff() {
VibratorService.vibratorOff(mNativeControllerPtr);
VibratorService.vibratorOff(mNativeServicePtr);
}
/** Sets the amplitude for the vibrator to run. */
public void vibratorSetAmplitude(int amplitude) {
VibratorService.vibratorSetAmplitude(mNativeControllerPtr, amplitude);
VibratorService.vibratorSetAmplitude(mNativeServicePtr, amplitude);
}
/** Returns all predefined effects supported by the device vibrator. */
public int[] vibratorGetSupportedEffects() {
return VibratorService.vibratorGetSupportedEffects(mNativeControllerPtr);
return VibratorService.vibratorGetSupportedEffects(mNativeServicePtr);
}
/** Returns all compose primitives supported by the device vibrator. */
public int[] vibratorGetSupportedPrimitives() {
return VibratorService.vibratorGetSupportedPrimitives(mNativeControllerPtr);
return VibratorService.vibratorGetSupportedPrimitives(mNativeServicePtr);
}
/** Turns vibrator on to perform one of the supported effects. */
public long vibratorPerformEffect(long effect, long strength, Vibration vibration) {
public long vibratorPerformEffect(long effect, long strength, long vibrationId) {
return VibratorService.vibratorPerformEffect(
mNativeControllerPtr, effect, strength, vibration);
mNativeServicePtr, effect, strength, vibrationId);
}
/** Turns vibrator on to perform one of the supported composed effects. */
public void vibratorPerformComposedEffect(
VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration) {
VibratorService.vibratorPerformComposedEffect(mNativeControllerPtr, effect, vibration);
VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) {
VibratorService.vibratorPerformComposedEffect(mNativeServicePtr, effect,
vibrationId);
}
/** Enabled the device vibrator to be controlled by another service. */
public void vibratorSetExternalControl(boolean enabled) {
VibratorService.vibratorSetExternalControl(mNativeControllerPtr, enabled);
VibratorService.vibratorSetExternalControl(mNativeServicePtr, enabled);
}
/** Returns all capabilities of the device vibrator. */
public long vibratorGetCapabilities() {
return VibratorService.vibratorGetCapabilities(mNativeControllerPtr);
return VibratorService.vibratorGetCapabilities(mNativeServicePtr);
}
/** Enable always-on vibration with given id and effect. */
public void vibratorAlwaysOnEnable(long id, long effect, long strength) {
VibratorService.vibratorAlwaysOnEnable(mNativeControllerPtr, id, effect, strength);
VibratorService.vibratorAlwaysOnEnable(mNativeServicePtr, id, effect, strength);
}
/** Disable always-on vibration for given id. */
public void vibratorAlwaysOnDisable(long id) {
VibratorService.vibratorAlwaysOnDisable(mNativeControllerPtr, id);
VibratorService.vibratorAlwaysOnDisable(mNativeServicePtr, id);
}
}

View File

@@ -40,14 +40,12 @@ namespace aidl = android::hardware::vibrator;
namespace android {
static JavaVM* sJvm = nullptr;
static jmethodID sMethodIdOnComplete;
static struct {
jfieldID id;
jfieldID scale;
jfieldID delay;
} gPrimitiveClassInfo;
} sPrimitiveClassInfo;
static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) ==
static_cast<uint8_t>(aidl::EffectStrength::LIGHT));
@@ -77,100 +75,117 @@ static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) ==
static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
static inline void callVibrationOnComplete(jobject vibration) {
if (vibration == nullptr) {
return;
class NativeVibratorService {
public:
NativeVibratorService(JNIEnv* env, jobject callbackListener)
: mController(std::make_unique<vibrator::HalController>()),
mCallbackListener(env->NewGlobalRef(callbackListener)) {
LOG_ALWAYS_FATAL_IF(mCallbackListener == nullptr,
"Unable to create global reference to vibration callback handler");
}
auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
jniEnv->CallVoidMethod(vibration, sMethodIdOnComplete);
jniEnv->DeleteGlobalRef(vibration);
}
~NativeVibratorService() {
auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
jniEnv->DeleteGlobalRef(mCallbackListener);
}
vibrator::HalController* controller() const { return mController.get(); }
std::function<void()> createCallback(jlong vibrationId) {
return [vibrationId, this]() {
auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, vibrationId);
};
}
private:
const std::unique_ptr<vibrator::HalController> mController;
const jobject mCallbackListener;
};
static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) {
aidl::CompositeEffect effect;
effect.primitive = static_cast<aidl::CompositePrimitive>(
env->GetIntField(primitive, gPrimitiveClassInfo.id));
effect.scale = static_cast<float>(env->GetFloatField(primitive, gPrimitiveClassInfo.scale));
effect.delayMs = static_cast<int32_t>(env->GetIntField(primitive, gPrimitiveClassInfo.delay));
env->GetIntField(primitive, sPrimitiveClassInfo.id));
effect.scale = static_cast<float>(env->GetFloatField(primitive, sPrimitiveClassInfo.scale));
effect.delayMs = static_cast<int32_t>(env->GetIntField(primitive, sPrimitiveClassInfo.delay));
return effect;
}
static void destroyVibratorController(void* rawVibratorController) {
vibrator::HalController* vibratorController =
reinterpret_cast<vibrator::HalController*>(rawVibratorController);
if (vibratorController) {
delete vibratorController;
static void destroyNativeService(void* servicePtr) {
NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
if (service) {
delete service;
}
}
static jlong vibratorInit(JNIEnv* /* env */, jclass /* clazz */) {
std::unique_ptr<vibrator::HalController> controller =
std::make_unique<vibrator::HalController>();
controller->init();
return reinterpret_cast<jlong>(controller.release());
static jlong vibratorInit(JNIEnv* env, jclass /* clazz */, jobject callbackListener) {
std::unique_ptr<NativeVibratorService> service =
std::make_unique<NativeVibratorService>(env, callbackListener);
service->controller()->init();
return reinterpret_cast<jlong>(service.release());
}
static jlong vibratorGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyVibratorController));
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeService));
}
static jboolean vibratorExists(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
if (controller == nullptr) {
ALOGE("vibratorExists failed because controller was not initialized");
static jboolean vibratorExists(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
if (service == nullptr) {
ALOGE("vibratorExists failed because native service was not initialized");
return JNI_FALSE;
}
return controller->ping().isOk() ? JNI_TRUE : JNI_FALSE;
return service->controller()->ping().isOk() ? JNI_TRUE : JNI_FALSE;
}
static void vibratorOn(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, jlong timeoutMs,
jobject vibration) {
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
if (controller == nullptr) {
ALOGE("vibratorOn failed because controller was not initialized");
static void vibratorOn(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong timeoutMs,
jlong vibrationId) {
NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
if (service == nullptr) {
ALOGE("vibratorOn failed because native service was not initialized");
return;
}
jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration);
auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); };
controller->on(std::chrono::milliseconds(timeoutMs), callback);
auto callback = service->createCallback(vibrationId);
service->controller()->on(std::chrono::milliseconds(timeoutMs), callback);
}
static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
if (controller == nullptr) {
ALOGE("vibratorOff failed because controller was not initialized");
static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
if (service == nullptr) {
ALOGE("vibratorOff failed because native service was not initialized");
return;
}
controller->off();
service->controller()->off();
}
static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong servicePtr,
jint amplitude) {
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
if (controller == nullptr) {
ALOGE("vibratorSetAmplitude failed because controller was not initialized");
NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
if (service == nullptr) {
ALOGE("vibratorSetAmplitude failed because native service was not initialized");
return;
}
controller->setAmplitude(static_cast<int32_t>(amplitude));
service->controller()->setAmplitude(static_cast<int32_t>(amplitude));
}
static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong servicePtr,
jboolean enabled) {
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
if (controller == nullptr) {
ALOGE("vibratorSetExternalControl failed because controller was not initialized");
NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
if (service == nullptr) {
ALOGE("vibratorSetExternalControl failed because native service was not initialized");
return;
}
controller->setExternalControl(enabled);
service->controller()->setExternalControl(enabled);
}
static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
if (controller == nullptr) {
ALOGE("vibratorGetSupportedEffects failed because controller was not initialized");
static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
if (service == nullptr) {
ALOGE("vibratorGetSupportedEffects failed because native service was not initialized");
return nullptr;
}
auto result = controller->getSupportedEffects();
auto result = service->controller()->getSupportedEffects();
if (!result.isOk()) {
return nullptr;
}
@@ -181,14 +196,13 @@ static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jl
return effects;
}
static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */,
jlong controllerPtr) {
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
if (controller == nullptr) {
ALOGE("vibratorGetSupportedPrimitives failed because controller was not initialized");
static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
if (service == nullptr) {
ALOGE("vibratorGetSupportedPrimitives failed because native service was not initialized");
return nullptr;
}
auto result = controller->getSupportedPrimitives();
auto result = service->controller()->getSupportedPrimitives();
if (!result.isOk()) {
return nullptr;
}
@@ -199,26 +213,25 @@ static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */,
return primitives;
}
static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
jlong effect, jlong strength, jobject vibration) {
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
if (controller == nullptr) {
ALOGE("vibratorPerformEffect failed because controller was not initialized");
static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong effect,
jlong strength, jlong vibrationId) {
NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
if (service == nullptr) {
ALOGE("vibratorPerformEffect failed because native service was not initialized");
return -1;
}
aidl::Effect effectType = static_cast<aidl::Effect>(effect);
aidl::EffectStrength effectStrength = static_cast<aidl::EffectStrength>(strength);
jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration);
auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); };
auto result = controller->performEffect(effectType, effectStrength, callback);
auto callback = service->createCallback(vibrationId);
auto result = service->controller()->performEffect(effectType, effectStrength, callback);
return result.isOk() ? result.value().count() : -1;
}
static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
jobjectArray composition, jobject vibration) {
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
if (controller == nullptr) {
ALOGE("vibratorPerformComposedEffect failed because controller was not initialized");
static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong servicePtr,
jobjectArray composition, jlong vibrationId) {
NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
if (service == nullptr) {
ALOGE("vibratorPerformComposedEffect failed because native service was not initialized");
return;
}
size_t size = env->GetArrayLength(composition);
@@ -227,54 +240,52 @@ static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong
jobject element = env->GetObjectArrayElement(composition, i);
effects.push_back(effectFromJavaPrimitive(env, element));
}
jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration);
auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); };
controller->performComposedEffect(effects, callback);
auto callback = service->createCallback(vibrationId);
service->controller()->performComposedEffect(effects, callback);
}
static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
if (controller == nullptr) {
ALOGE("vibratorGetCapabilities failed because controller was not initialized");
static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
if (service == nullptr) {
ALOGE("vibratorGetCapabilities failed because native service was not initialized");
return 0;
}
auto result = controller->getCapabilities();
auto result = service->controller()->getCapabilities();
return result.isOk() ? static_cast<jlong>(result.value()) : 0;
}
static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, jlong id,
static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong id,
jlong effect, jlong strength) {
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
if (controller == nullptr) {
ALOGE("vibratorAlwaysOnEnable failed because controller was not initialized");
NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
if (service == nullptr) {
ALOGE("vibratorAlwaysOnEnable failed because native service was not initialized");
return;
}
controller->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect),
static_cast<aidl::EffectStrength>(strength));
service->controller()->alwaysOnEnable(static_cast<int32_t>(id),
static_cast<aidl::Effect>(effect),
static_cast<aidl::EffectStrength>(strength));
}
static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
jlong id) {
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
if (controller == nullptr) {
ALOGE("vibratorAlwaysOnDisable failed because controller was not initialized");
static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong id) {
NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
if (service == nullptr) {
ALOGE("vibratorAlwaysOnDisable failed because native service was not initialized");
return;
}
controller->alwaysOnDisable(static_cast<int32_t>(id));
service->controller()->alwaysOnDisable(static_cast<int32_t>(id));
}
static const JNINativeMethod method_table[] = {
{"vibratorInit", "()J", (void*)vibratorInit},
{"vibratorInit", "(Lcom/android/server/VibratorService$OnCompleteListener;)J",
(void*)vibratorInit},
{"vibratorGetFinalizer", "()J", (void*)vibratorGetFinalizer},
{"vibratorExists", "(J)Z", (void*)vibratorExists},
{"vibratorOn", "(JJLcom/android/server/VibratorService$Vibration;)V", (void*)vibratorOn},
{"vibratorOn", "(JJJ)V", (void*)vibratorOn},
{"vibratorOff", "(J)V", (void*)vibratorOff},
{"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude},
{"vibratorPerformEffect", "(JJJLcom/android/server/VibratorService$Vibration;)J",
(void*)vibratorPerformEffect},
{"vibratorPerformEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
{"vibratorPerformComposedEffect",
"(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/"
"VibratorService$Vibration;)V",
"(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)V",
(void*)vibratorPerformComposedEffect},
{"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
{"vibratorGetSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives},
@@ -284,18 +295,17 @@ static const JNINativeMethod method_table[] = {
{"vibratorAlwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
};
int register_android_server_VibratorService(JavaVM* vm, JNIEnv* env) {
sJvm = vm;
sMethodIdOnComplete =
GetMethodIDOrDie(env,
FindClassOrDie(env, "com/android/server/VibratorService$Vibration"),
"onComplete", "()V");
int register_android_server_VibratorService(JavaVM* jvm, JNIEnv* env) {
sJvm = jvm;
jclass listenerClass =
FindClassOrDie(env, "com/android/server/VibratorService$OnCompleteListener");
sMethodIdOnComplete = GetMethodIDOrDie(env, listenerClass, "onComplete", "(J)V");
jclass primitiveClass =
FindClassOrDie(env, "android/os/VibrationEffect$Composition$PrimitiveEffect");
gPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "id", "I");
gPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F");
gPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I");
sPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "id", "I");
sPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F");
sPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I");
return jniRegisterNativeMethods(env, "com/android/server/VibratorService", method_table,
NELEM(method_table));

View File

@@ -20,12 +20,13 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalMatchers.gt;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.intThat;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
@@ -167,7 +168,7 @@ public class VibratorServiceTest {
@Test
public void createService_initializesNativeService() {
createService();
verify(mNativeWrapperMock).vibratorInit();
verify(mNativeWrapperMock).vibratorInit(notNull());
verify(mNativeWrapperMock).vibratorOff();
}
@@ -294,7 +295,7 @@ public class VibratorServiceTest {
assertTrue(service.isVibrating());
verify(mNativeWrapperMock).vibratorOff();
verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class));
verify(mNativeWrapperMock).vibratorOn(eq(100L), gt(0L));
verify(mNativeWrapperMock).vibratorSetAmplitude(eq(128));
}
@@ -307,7 +308,7 @@ public class VibratorServiceTest {
assertTrue(service.isVibrating());
verify(mNativeWrapperMock).vibratorOff();
verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class));
verify(mNativeWrapperMock).vibratorOn(eq(100L), gt(0L));
verify(mNativeWrapperMock, never()).vibratorSetAmplitude(anyInt());
}
@@ -321,10 +322,8 @@ public class VibratorServiceTest {
vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
verify(mNativeWrapperMock).vibratorOff();
verify(mNativeWrapperMock).vibratorPerformEffect(
eq((long) VibrationEffect.EFFECT_CLICK),
eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG),
any(VibratorService.Vibration.class));
verify(mNativeWrapperMock).vibratorPerformEffect(eq((long) VibrationEffect.EFFECT_CLICK),
eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), gt(0L));
}
@Test
@@ -343,7 +342,7 @@ public class VibratorServiceTest {
verify(mNativeWrapperMock).vibratorOff();
verify(mNativeWrapperMock).vibratorPerformComposedEffect(
primitivesCaptor.capture(), any(VibratorService.Vibration.class));
primitivesCaptor.capture(), gt(0L));
// Check all primitive effect fields are passed down to the HAL.
assertEquals(1, primitivesCaptor.getValue().length);
@@ -368,7 +367,7 @@ public class VibratorServiceTest {
// Wait for VibrateThread to turn vibrator ON with total timing and no callback.
Thread.sleep(5);
verify(mNativeWrapperMock).vibratorOn(eq(30L), isNull());
verify(mNativeWrapperMock).vibratorOn(eq(30L), eq(0L));
// First amplitude set right away.
verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100));
@@ -384,11 +383,11 @@ public class VibratorServiceTest {
@Test
public void vibrate_withOneShotAndNativeCallbackTriggered_finishesVibration() {
doAnswer(invocation -> {
((VibratorService.Vibration) invocation.getArgument(1)).onComplete();
return null;
}).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class));
VibratorService service = createService();
doAnswer(invocation -> {
service.onVibrationComplete(invocation.getArgument(1));
return null;
}).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong());
Mockito.clearInvocations(mNativeWrapperMock);
vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
@@ -396,7 +395,7 @@ public class VibratorServiceTest {
InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(100L),
any(VibratorService.Vibration.class));
gt(0L));
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
}
@@ -404,12 +403,11 @@ public class VibratorServiceTest {
public void vibrate_withPrebakedAndNativeCallbackTriggered_finishesVibration() {
when(mNativeWrapperMock.vibratorGetSupportedEffects())
.thenReturn(new int[]{VibrationEffect.EFFECT_CLICK});
doAnswer(invocation -> {
((VibratorService.Vibration) invocation.getArgument(2)).onComplete();
return 10_000L; // 10s
}).when(mNativeWrapperMock).vibratorPerformEffect(
anyLong(), anyLong(), any(VibratorService.Vibration.class));
VibratorService service = createService();
doAnswer(invocation -> {
service.onVibrationComplete(invocation.getArgument(2));
return 10_000L; // 10s
}).when(mNativeWrapperMock).vibratorPerformEffect(anyLong(), anyLong(), anyLong());
Mockito.clearInvocations(mNativeWrapperMock);
vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
@@ -419,7 +417,7 @@ public class VibratorServiceTest {
inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformEffect(
eq((long) VibrationEffect.EFFECT_CLICK),
eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG),
any(VibratorService.Vibration.class));
gt(0L));
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
}
@@ -436,20 +434,19 @@ public class VibratorServiceTest {
Thread.sleep(15);
InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(3L), isNull());
inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(2L), isNull());
inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(3L), eq(0L));
inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(2L), eq(0L));
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
}
@Test
public void vibrate_withComposedAndNativeCallbackTriggered_finishesVibration() {
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
doAnswer(invocation -> {
((VibratorService.Vibration) invocation.getArgument(1)).onComplete();
return null;
}).when(mNativeWrapperMock).vibratorPerformComposedEffect(
any(), any(VibratorService.Vibration.class));
VibratorService service = createService();
doAnswer(invocation -> {
service.onVibrationComplete(invocation.getArgument(1));
return null;
}).when(mNativeWrapperMock).vibratorPerformComposedEffect(any(), anyLong());
Mockito.clearInvocations(mNativeWrapperMock);
VibrationEffect effect = VibrationEffect.startComposition()
@@ -460,32 +457,7 @@ public class VibratorServiceTest {
InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect(
any(VibrationEffect.Composition.PrimitiveEffect[].class),
any(VibratorService.Vibration.class));
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
}
@Test
public void vibrate_whenBinderDies_cancelsVibration() {
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
doAnswer(invocation -> {
((VibratorService.Vibration) invocation.getArgument(1)).binderDied();
return null;
}).when(mNativeWrapperMock).vibratorPerformComposedEffect(
any(), any(VibratorService.Vibration.class));
VibratorService service = createService();
Mockito.clearInvocations(mNativeWrapperMock);
VibrationEffect effect = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10)
.compose();
vibrate(service, effect);
InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect(
any(VibrationEffect.Composition.PrimitiveEffect[].class),
any(VibratorService.Vibration.class));
any(VibrationEffect.Composition.PrimitiveEffect[].class), gt(0L));
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
}
@@ -513,12 +485,11 @@ public class VibratorServiceTest {
@Test
public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
doAnswer(invocation -> {
((VibratorService.Vibration) invocation.getArgument(1)).onComplete();
return null;
}).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class));
VibratorService service = createService();
doAnswer(invocation -> {
service.onVibrationComplete(invocation.getArgument(1));
return null;
}).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong());
service.registerVibratorStateListener(mVibratorStateListenerMock);
verify(mVibratorStateListenerMock).onVibrating(false);
Mockito.clearInvocations(mVibratorStateListenerMock);
@@ -569,15 +540,15 @@ public class VibratorServiceTest {
verify(mNativeWrapperMock).vibratorPerformEffect(
eq((long) VibrationEffect.EFFECT_CLICK),
eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any());
eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), anyLong());
verify(mNativeWrapperMock).vibratorPerformEffect(
eq((long) VibrationEffect.EFFECT_TICK),
eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), any());
eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), anyLong());
verify(mNativeWrapperMock).vibratorPerformEffect(
eq((long) VibrationEffect.EFFECT_DOUBLE_CLICK),
eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), any());
eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), anyLong());
verify(mNativeWrapperMock, never()).vibratorPerformEffect(
eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), any());
eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), anyLong());
}
@Test
@@ -644,7 +615,7 @@ public class VibratorServiceTest {
// Ringtone vibration is off, so only the other 3 are propagated to native.
verify(mNativeWrapperMock, times(3)).vibratorPerformComposedEffect(
primitivesCaptor.capture(), any());
primitivesCaptor.capture(), anyLong());
List<VibrationEffect.Composition.PrimitiveEffect[]> values =
primitivesCaptor.getAllValues();