Merge "Change VibratorInfo reload logic in VibratorManagerService"
This commit is contained in:
committed by
Android (Google) Code Review
commit
da37e2444c
@@ -51,6 +51,7 @@ public class SystemVibrator extends Vibrator {
|
||||
mRegisteredListeners = new ArrayMap<>();
|
||||
|
||||
private final Object mLock = new Object();
|
||||
@GuardedBy("mLock")
|
||||
private AllVibratorsInfo mVibratorInfo;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
@@ -73,7 +74,15 @@ public class SystemVibrator extends Vibrator {
|
||||
int[] vibratorIds = mVibratorManager.getVibratorIds();
|
||||
VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length];
|
||||
for (int i = 0; i < vibratorIds.length; i++) {
|
||||
vibratorInfos[i] = mVibratorManager.getVibrator(vibratorIds[i]).getInfo();
|
||||
Vibrator vibrator = mVibratorManager.getVibrator(vibratorIds[i]);
|
||||
if (vibrator instanceof NullVibrator) {
|
||||
Log.w(TAG, "Vibrator manager service not ready; "
|
||||
+ "Info not yet available for vibrator: " + vibratorIds[i]);
|
||||
// This should never happen after the vibrator manager service is ready.
|
||||
// Skip caching this vibrator until then.
|
||||
return VibratorInfo.EMPTY_VIBRATOR_INFO;
|
||||
}
|
||||
vibratorInfos[i] = vibrator.getInfo();
|
||||
}
|
||||
return mVibratorInfo = new AllVibratorsInfo(vibratorInfos);
|
||||
}
|
||||
|
||||
@@ -165,7 +165,11 @@ public abstract class Vibrator {
|
||||
return ctx != null ? ctx.getResources().getFloat(resId) : defaultValue;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
/**
|
||||
* Get the info describing this vibrator.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
protected VibratorInfo getInfo() {
|
||||
return VibratorInfo.EMPTY_VIBRATOR_INFO;
|
||||
}
|
||||
|
||||
@@ -461,7 +461,8 @@ public class VibratorInfo implements Parcelable {
|
||||
int supportedPrimitivesCount = mSupportedPrimitives.size();
|
||||
String[] names = new String[supportedPrimitivesCount];
|
||||
for (int i = 0; i < supportedPrimitivesCount; i++) {
|
||||
names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i));
|
||||
names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i))
|
||||
+ "(" + mSupportedPrimitives.valueAt(i) + "ms)";
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
@@ -41,12 +41,11 @@ final class VibratorController {
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final NativeWrapper mNativeWrapper;
|
||||
private final VibratorInfo.Builder mVibratorInfoBuilder;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private VibratorInfo mVibratorInfo;
|
||||
@GuardedBy("mLock")
|
||||
private boolean mVibratorInfoLoaded;
|
||||
private boolean mVibratorInfoLoadSuccessful;
|
||||
@GuardedBy("mLock")
|
||||
private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
|
||||
new RemoteCallbackList<>();
|
||||
@@ -73,10 +72,16 @@ final class VibratorController {
|
||||
NativeWrapper nativeWrapper) {
|
||||
mNativeWrapper = nativeWrapper;
|
||||
mNativeWrapper.init(vibratorId, listener);
|
||||
mVibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
|
||||
mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
|
||||
mVibratorInfoBuilder);
|
||||
mVibratorInfo = mVibratorInfoBuilder.build();
|
||||
VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
|
||||
mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
|
||||
vibratorInfoBuilder);
|
||||
mVibratorInfo = vibratorInfoBuilder.build();
|
||||
|
||||
if (!mVibratorInfoLoadSuccessful) {
|
||||
Slog.e(TAG,
|
||||
"Vibrator controller initialization failed to load some HAL info for vibrator "
|
||||
+ vibratorId);
|
||||
}
|
||||
}
|
||||
|
||||
/** Register state listener for this vibrator. */
|
||||
@@ -108,15 +113,33 @@ final class VibratorController {
|
||||
}
|
||||
}
|
||||
|
||||
/** Reruns the query to the vibrator to load the {@link VibratorInfo}, if not yet successful. */
|
||||
public void reloadVibratorInfoIfNeeded() {
|
||||
synchronized (mLock) {
|
||||
if (mVibratorInfoLoadSuccessful) {
|
||||
return;
|
||||
}
|
||||
int vibratorId = mVibratorInfo.getId();
|
||||
VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
|
||||
mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
|
||||
vibratorInfoBuilder);
|
||||
mVibratorInfo = vibratorInfoBuilder.build();
|
||||
if (!mVibratorInfoLoadSuccessful) {
|
||||
Slog.e(TAG, "Failed retry of HAL getInfo for vibrator " + vibratorId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks if the {@link VibratorInfo} was loaded from the vibrator hardware successfully. */
|
||||
boolean isVibratorInfoLoadSuccessful() {
|
||||
synchronized (mLock) {
|
||||
return mVibratorInfoLoadSuccessful;
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */
|
||||
public VibratorInfo getVibratorInfo() {
|
||||
synchronized (mLock) {
|
||||
if (!mVibratorInfoLoaded) {
|
||||
// Try to load the vibrator metadata that has failed in the last attempt.
|
||||
mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
|
||||
mVibratorInfoBuilder);
|
||||
mVibratorInfo = mVibratorInfoBuilder.build();
|
||||
}
|
||||
return mVibratorInfo;
|
||||
}
|
||||
}
|
||||
@@ -164,7 +187,9 @@ final class VibratorController {
|
||||
* @return true if this vibrator has this capability, false otherwise
|
||||
*/
|
||||
public boolean hasCapability(long capability) {
|
||||
return mVibratorInfo.hasCapability(capability);
|
||||
synchronized (mLock) {
|
||||
return mVibratorInfo.hasCapability(capability);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return {@code true} if the underlying vibrator is currently available, false otherwise. */
|
||||
@@ -178,10 +203,10 @@ final class VibratorController {
|
||||
* <p>This will affect the state of {@link #isUnderExternalControl()}.
|
||||
*/
|
||||
public void setExternalControl(boolean externalControl) {
|
||||
if (!mVibratorInfo.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
|
||||
return;
|
||||
}
|
||||
synchronized (mLock) {
|
||||
if (!mVibratorInfo.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
|
||||
return;
|
||||
}
|
||||
mIsUnderExternalControl = externalControl;
|
||||
mNativeWrapper.setExternalControl(externalControl);
|
||||
}
|
||||
@@ -192,10 +217,10 @@ final class VibratorController {
|
||||
* if given {@code effect} is {@code null}.
|
||||
*/
|
||||
public void updateAlwaysOn(int id, @Nullable PrebakedSegment prebaked) {
|
||||
if (!mVibratorInfo.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
|
||||
return;
|
||||
}
|
||||
synchronized (mLock) {
|
||||
if (!mVibratorInfo.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
|
||||
return;
|
||||
}
|
||||
if (prebaked == null) {
|
||||
mNativeWrapper.alwaysOnDisable(id);
|
||||
} else {
|
||||
@@ -268,10 +293,10 @@ final class VibratorController {
|
||||
* do not support the input or a negative number if the operation failed.
|
||||
*/
|
||||
public long on(PrimitiveSegment[] primitives, long vibrationId) {
|
||||
if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
|
||||
return 0;
|
||||
}
|
||||
synchronized (mLock) {
|
||||
if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
|
||||
return 0;
|
||||
}
|
||||
long duration = mNativeWrapper.compose(primitives, vibrationId);
|
||||
if (duration > 0) {
|
||||
mCurrentAmplitude = -1;
|
||||
@@ -290,10 +315,10 @@ final class VibratorController {
|
||||
* @return The duration of the effect playing, or 0 if unsupported.
|
||||
*/
|
||||
public long on(RampSegment[] primitives, long vibrationId) {
|
||||
if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
|
||||
return 0;
|
||||
}
|
||||
synchronized (mLock) {
|
||||
if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
|
||||
return 0;
|
||||
}
|
||||
int braking = mVibratorInfo.getDefaultBraking();
|
||||
long duration = mNativeWrapper.composePwle(primitives, braking, vibrationId);
|
||||
if (duration > 0) {
|
||||
@@ -327,6 +352,7 @@ final class VibratorController {
|
||||
synchronized (mLock) {
|
||||
return "VibratorController{"
|
||||
+ "mVibratorInfo=" + mVibratorInfo
|
||||
+ ", mVibratorInfoLoadSuccessful=" + mVibratorInfoLoadSuccessful
|
||||
+ ", mIsVibrating=" + mIsVibrating
|
||||
+ ", mCurrentAmplitude=" + mCurrentAmplitude
|
||||
+ ", mIsUnderExternalControl=" + mIsUnderExternalControl
|
||||
@@ -393,10 +419,15 @@ final class VibratorController {
|
||||
* allocated and returned by {@link #nativeInit(int, OnVibrationCompleteListener)}.
|
||||
*/
|
||||
private static native long getNativeFinalizer();
|
||||
|
||||
private static native boolean isAvailable(long nativePtr);
|
||||
|
||||
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 long performEffect(long nativePtr, long effect, long strength,
|
||||
long vibrationId);
|
||||
|
||||
|
||||
@@ -128,6 +128,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
||||
private VibrationThread mNextVibration;
|
||||
@GuardedBy("mLock")
|
||||
private ExternalVibrationHolder mCurrentExternalVibration;
|
||||
@GuardedBy("mLock")
|
||||
private boolean mServiceReady;
|
||||
|
||||
private final VibrationSettings mVibrationSettings;
|
||||
private final VibrationScaler mVibrationScaler;
|
||||
@@ -201,6 +203,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
||||
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
|
||||
mWakeLock.setReferenceCounted(true);
|
||||
|
||||
// Load vibrator hardware info. The vibrator ids and manager capabilities are loaded only
|
||||
// once and assumed unchanged for the lifecycle of this service. Each individual vibrator
|
||||
// can still retry loading each individual vibrator hardware spec once more at systemReady.
|
||||
mCapabilities = mNativeWrapper.getCapabilities();
|
||||
int[] vibratorIds = mNativeWrapper.getVibratorIds();
|
||||
if (vibratorIds == null) {
|
||||
@@ -235,6 +240,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
||||
Slog.v(TAG, "Initializing VibratorManager service...");
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "systemReady");
|
||||
try {
|
||||
// Will retry to load each vibrator's info, if any request have failed.
|
||||
for (int i = 0; i < mVibrators.size(); i++) {
|
||||
mVibrators.valueAt(i).reloadVibratorInfoIfNeeded();
|
||||
}
|
||||
|
||||
mVibrationSettings.onSystemReady();
|
||||
mInputDeviceDelegate.onSystemReady();
|
||||
|
||||
@@ -243,6 +253,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
||||
// Will update settings and input devices.
|
||||
updateServiceState();
|
||||
} finally {
|
||||
synchronized (mLock) {
|
||||
mServiceReady = true;
|
||||
}
|
||||
Slog.v(TAG, "VibratorManager service initialized");
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
}
|
||||
@@ -256,8 +269,19 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
||||
@Override // Binder call
|
||||
@Nullable
|
||||
public VibratorInfo getVibratorInfo(int vibratorId) {
|
||||
VibratorController controller = mVibrators.get(vibratorId);
|
||||
return controller == null ? null : controller.getVibratorInfo();
|
||||
final VibratorController controller = mVibrators.get(vibratorId);
|
||||
if (controller == null) {
|
||||
return null;
|
||||
}
|
||||
final VibratorInfo info = controller.getVibratorInfo();
|
||||
synchronized (mLock) {
|
||||
if (mServiceReady) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
// If the service is not ready and the load was unsuccessful then return null while waiting
|
||||
// for the service to be ready. It will retry to load the complete info from the HAL.
|
||||
return controller.isVibratorInfoLoadSuccessful() ? info : null;
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
|
||||
@@ -443,7 +443,7 @@ static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
|
||||
env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyMapping,
|
||||
frequencyMapping);
|
||||
|
||||
return info.checkAndLogFailure("vibratorGetInfo") ? JNI_FALSE : JNI_TRUE;
|
||||
return info.isFailedLogged("vibratorGetInfo") ? JNI_FALSE : JNI_TRUE;
|
||||
}
|
||||
|
||||
static const JNINativeMethod method_table[] = {
|
||||
|
||||
@@ -51,6 +51,7 @@ final class FakeVibratorControllerProvider {
|
||||
private final FakeNativeWrapper mNativeWrapper;
|
||||
|
||||
private boolean mIsAvailable = true;
|
||||
private boolean mIsInfoLoadSuccessful = true;
|
||||
private long mLatency;
|
||||
private int mOffCount;
|
||||
|
||||
@@ -172,7 +173,7 @@ final class FakeVibratorControllerProvider {
|
||||
infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping(mMinFrequency,
|
||||
mResonantFrequency, mFrequencyResolution, suggestedFrequencyRange,
|
||||
mMaxAmplitudes));
|
||||
return true;
|
||||
return mIsInfoLoadSuccessful;
|
||||
}
|
||||
|
||||
private void applyLatency() {
|
||||
@@ -212,6 +213,14 @@ final class FakeVibratorControllerProvider {
|
||||
mIsAvailable = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the result for the method that loads the {@link VibratorInfo}, for faking a vibrator
|
||||
* that fails to load some of the hardware data.
|
||||
*/
|
||||
public void setVibratorInfoLoadSuccessful(boolean successful) {
|
||||
mIsInfoLoadSuccessful = successful;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the latency this controller should fake for turning the vibrator hardware on or setting
|
||||
* it's vibration amplitude.
|
||||
|
||||
@@ -287,7 +287,26 @@ public class VibratorManagerServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVibratorInfo_withExistingVibratorId_returnsHalInfoForVibrator() {
|
||||
public void getVibratorInfo_vibratorFailedLoadBeforeSystemReady_returnsNull() {
|
||||
mockVibrators(1);
|
||||
mVibratorProviders.get(1).setVibratorInfoLoadSuccessful(false);
|
||||
assertNull(createService().getVibratorInfo(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVibratorInfo_vibratorFailedLoadAfterSystemReady_returnsInfoForVibrator() {
|
||||
mockVibrators(1);
|
||||
mVibratorProviders.get(1).setVibratorInfoLoadSuccessful(false);
|
||||
mVibratorProviders.get(1).setResonantFrequency(123.f);
|
||||
VibratorInfo info = createSystemReadyService().getVibratorInfo(1);
|
||||
|
||||
assertNotNull(info);
|
||||
assertEquals(1, info.getId());
|
||||
assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVibratorInfo_vibratorSuccessfulLoadBeforeSystemReady_returnsInfoForVibrator() {
|
||||
mockVibrators(1);
|
||||
FakeVibratorControllerProvider vibrator = mVibratorProviders.get(1);
|
||||
vibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS, IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
@@ -295,7 +314,7 @@ public class VibratorManagerServiceTest {
|
||||
vibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK);
|
||||
vibrator.setResonantFrequency(123.f);
|
||||
vibrator.setQFactor(Float.NaN);
|
||||
VibratorInfo info = createSystemReadyService().getVibratorInfo(1);
|
||||
VibratorInfo info = createService().getVibratorInfo(1);
|
||||
|
||||
assertNotNull(info);
|
||||
assertEquals(1, info.getId());
|
||||
@@ -312,6 +331,24 @@ public class VibratorManagerServiceTest {
|
||||
assertTrue(Float.isNaN(info.getQFactor()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVibratorInfo_vibratorFailedThenSuccessfulLoad_returnsNullThenInfo() {
|
||||
mockVibrators(1);
|
||||
mVibratorProviders.get(1).setVibratorInfoLoadSuccessful(false);
|
||||
|
||||
VibratorManagerService service = createService();
|
||||
assertNull(createService().getVibratorInfo(1));
|
||||
|
||||
mVibratorProviders.get(1).setVibratorInfoLoadSuccessful(true);
|
||||
mVibratorProviders.get(1).setResonantFrequency(123.f);
|
||||
service.systemReady();
|
||||
|
||||
VibratorInfo info = createService().getVibratorInfo(1);
|
||||
assertNotNull(info);
|
||||
assertEquals(1, info.getId());
|
||||
assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
|
||||
mockVibrators(1);
|
||||
|
||||
Reference in New Issue
Block a user