1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.vibrator; 18 19 import android.annotation.Nullable; 20 import android.hardware.vibrator.IVibrator; 21 import android.os.Binder; 22 import android.os.IVibratorStateListener; 23 import android.os.RemoteCallbackList; 24 import android.os.RemoteException; 25 import android.os.VibratorInfo; 26 import android.os.vibrator.PrebakedSegment; 27 import android.os.vibrator.PrimitiveSegment; 28 import android.os.vibrator.RampSegment; 29 import android.util.Slog; 30 31 import com.android.internal.annotations.GuardedBy; 32 import com.android.internal.annotations.VisibleForTesting; 33 34 import libcore.util.NativeAllocationRegistry; 35 36 /** Controls a single vibrator. */ 37 final class VibratorController { 38 private static final String TAG = "VibratorController"; 39 40 private final Object mLock = new Object(); 41 42 @GuardedBy("mLock") 43 private final NativeWrapper mNativeWrapper; 44 45 // Vibrator state listeners that support concurrent updates and broadcasts, but should lock 46 // while broadcasting to guarantee delivery order. 47 private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners = 48 new RemoteCallbackList<>(); 49 50 // Vibrator state variables that are updated from synchronized blocks but can be read anytime 51 // for a snippet of the current known vibrator state/info. 52 private volatile VibratorInfo mVibratorInfo; 53 private volatile boolean mVibratorInfoLoadSuccessful; 54 private volatile boolean mIsVibrating; 55 private volatile boolean mIsUnderExternalControl; 56 private volatile float mCurrentAmplitude; 57 58 /** Listener for vibration completion callbacks from native. */ 59 public interface OnVibrationCompleteListener { 60 61 /** Callback triggered when vibration is complete. */ onComplete(int vibratorId, long vibrationId)62 void onComplete(int vibratorId, long vibrationId); 63 } 64 VibratorController(int vibratorId, OnVibrationCompleteListener listener)65 VibratorController(int vibratorId, OnVibrationCompleteListener listener) { 66 this(vibratorId, listener, new NativeWrapper()); 67 } 68 69 @VisibleForTesting VibratorController(int vibratorId, OnVibrationCompleteListener listener, NativeWrapper nativeWrapper)70 VibratorController(int vibratorId, OnVibrationCompleteListener listener, 71 NativeWrapper nativeWrapper) { 72 mNativeWrapper = nativeWrapper; 73 mNativeWrapper.init(vibratorId, listener); 74 VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId); 75 mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(vibratorInfoBuilder); 76 mVibratorInfo = vibratorInfoBuilder.build(); 77 78 if (!mVibratorInfoLoadSuccessful) { 79 Slog.e(TAG, 80 "Vibrator controller initialization failed to load some HAL info for vibrator " 81 + vibratorId); 82 } 83 } 84 85 /** Register state listener for this vibrator. */ registerVibratorStateListener(IVibratorStateListener listener)86 public boolean registerVibratorStateListener(IVibratorStateListener listener) { 87 final long token = Binder.clearCallingIdentity(); 88 try { 89 // Register the listener and send the first state atomically, to avoid potentially 90 // out of order broadcasts in between. 91 synchronized (mLock) { 92 if (!mVibratorStateListeners.register(listener)) { 93 return false; 94 } 95 // Notify its callback after new client registered. 96 notifyStateListener(listener, mIsVibrating); 97 } 98 return true; 99 } finally { 100 Binder.restoreCallingIdentity(token); 101 } 102 } 103 104 /** Remove registered state listener for this vibrator. */ unregisterVibratorStateListener(IVibratorStateListener listener)105 public boolean unregisterVibratorStateListener(IVibratorStateListener listener) { 106 final long token = Binder.clearCallingIdentity(); 107 try { 108 return mVibratorStateListeners.unregister(listener); 109 } finally { 110 Binder.restoreCallingIdentity(token); 111 } 112 } 113 114 /** Reruns the query to the vibrator to load the {@link VibratorInfo}, if not yet successful. */ reloadVibratorInfoIfNeeded()115 public void reloadVibratorInfoIfNeeded() { 116 // Early check outside lock, for quick return. 117 if (mVibratorInfoLoadSuccessful) { 118 return; 119 } 120 synchronized (mLock) { 121 if (mVibratorInfoLoadSuccessful) { 122 return; 123 } 124 int vibratorId = mVibratorInfo.getId(); 125 VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId); 126 mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(vibratorInfoBuilder); 127 mVibratorInfo = vibratorInfoBuilder.build(); 128 if (!mVibratorInfoLoadSuccessful) { 129 Slog.e(TAG, "Failed retry of HAL getInfo for vibrator " + vibratorId); 130 } 131 } 132 } 133 134 /** Checks if the {@link VibratorInfo} was loaded from the vibrator hardware successfully. */ isVibratorInfoLoadSuccessful()135 boolean isVibratorInfoLoadSuccessful() { 136 return mVibratorInfoLoadSuccessful; 137 } 138 139 /** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */ getVibratorInfo()140 public VibratorInfo getVibratorInfo() { 141 return mVibratorInfo; 142 } 143 144 /** 145 * Return {@code true} is this vibrator is currently vibrating, false otherwise. 146 * 147 * <p>This state is controlled by calls to {@link #on} and {@link #off} methods, and is 148 * automatically notified to any registered {@link IVibratorStateListener} on change. 149 */ isVibrating()150 public boolean isVibrating() { 151 return mIsVibrating; 152 } 153 154 /** 155 * Returns the current amplitude the device is vibrating. 156 * 157 * <p>This value is set to 1 by the method {@link #on(long, long)}, and can be updated via 158 * {@link #setAmplitude(float)} if called while the device is vibrating. 159 * 160 * <p>If the device is vibrating via any other {@link #on} method then the current amplitude is 161 * unknown and this will return -1. 162 * 163 * <p>If {@link #isVibrating()} is false then this will be zero. 164 */ getCurrentAmplitude()165 public float getCurrentAmplitude() { 166 return mCurrentAmplitude; 167 } 168 169 /** Return {@code true} if this vibrator is under external control, false otherwise. */ isUnderExternalControl()170 public boolean isUnderExternalControl() { 171 return mIsUnderExternalControl; 172 } 173 174 /** 175 * Check against this vibrator capabilities. 176 * 177 * @param capability one of IVibrator.CAP_* 178 * @return true if this vibrator has this capability, false otherwise 179 */ hasCapability(long capability)180 public boolean hasCapability(long capability) { 181 return mVibratorInfo.hasCapability(capability); 182 } 183 184 /** Return {@code true} if the underlying vibrator is currently available, false otherwise. */ isAvailable()185 public boolean isAvailable() { 186 synchronized (mLock) { 187 return mNativeWrapper.isAvailable(); 188 } 189 } 190 191 /** 192 * Set the vibrator control to be external or not, based on given flag. 193 * 194 * <p>This will affect the state of {@link #isUnderExternalControl()}. 195 */ setExternalControl(boolean externalControl)196 public void setExternalControl(boolean externalControl) { 197 if (!mVibratorInfo.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { 198 return; 199 } 200 synchronized (mLock) { 201 mIsUnderExternalControl = externalControl; 202 mNativeWrapper.setExternalControl(externalControl); 203 } 204 } 205 206 /** 207 * Update the predefined vibration effect saved with given id. This will remove the saved effect 208 * if given {@code effect} is {@code null}. 209 */ updateAlwaysOn(int id, @Nullable PrebakedSegment prebaked)210 public void updateAlwaysOn(int id, @Nullable PrebakedSegment prebaked) { 211 if (!mVibratorInfo.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) { 212 return; 213 } 214 synchronized (mLock) { 215 if (prebaked == null) { 216 mNativeWrapper.alwaysOnDisable(id); 217 } else { 218 mNativeWrapper.alwaysOnEnable(id, prebaked.getEffectId(), 219 prebaked.getEffectStrength()); 220 } 221 } 222 } 223 224 /** Set the vibration amplitude. This will NOT affect the state of {@link #isVibrating()}. */ setAmplitude(float amplitude)225 public void setAmplitude(float amplitude) { 226 synchronized (mLock) { 227 if (mVibratorInfo.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) { 228 mNativeWrapper.setAmplitude(amplitude); 229 } 230 if (mIsVibrating) { 231 mCurrentAmplitude = amplitude; 232 } 233 } 234 } 235 236 /** 237 * Turn on the vibrator for {@code milliseconds} time, using {@code vibrationId} or completion 238 * callback to {@link OnVibrationCompleteListener}. 239 * 240 * <p>This will affect the state of {@link #isVibrating()}. 241 * 242 * @return The positive duration of the vibration started, if successful, zero if the vibrator 243 * do not support the input or a negative number if the operation failed. 244 */ on(long milliseconds, long vibrationId)245 public long on(long milliseconds, long vibrationId) { 246 synchronized (mLock) { 247 long duration = mNativeWrapper.on(milliseconds, vibrationId); 248 if (duration > 0) { 249 mCurrentAmplitude = -1; 250 notifyListenerOnVibrating(true); 251 } 252 return duration; 253 } 254 } 255 256 /** 257 * Plays predefined vibration effect, using {@code vibrationId} or completion callback to 258 * {@link OnVibrationCompleteListener}. 259 * 260 * <p>This will affect the state of {@link #isVibrating()}. 261 * 262 * @return The positive duration of the vibration started, if successful, zero if the vibrator 263 * do not support the input or a negative number if the operation failed. 264 */ on(PrebakedSegment prebaked, long vibrationId)265 public long on(PrebakedSegment prebaked, long vibrationId) { 266 synchronized (mLock) { 267 long duration = mNativeWrapper.perform(prebaked.getEffectId(), 268 prebaked.getEffectStrength(), vibrationId); 269 if (duration > 0) { 270 mCurrentAmplitude = -1; 271 notifyListenerOnVibrating(true); 272 } 273 return duration; 274 } 275 } 276 277 /** 278 * Plays a composition of vibration primitives, using {@code vibrationId} or completion callback 279 * to {@link OnVibrationCompleteListener}. 280 * 281 * <p>This will affect the state of {@link #isVibrating()}. 282 * 283 * @return The positive duration of the vibration started, if successful, zero if the vibrator 284 * do not support the input or a negative number if the operation failed. 285 */ on(PrimitiveSegment[] primitives, long vibrationId)286 public long on(PrimitiveSegment[] primitives, long vibrationId) { 287 if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { 288 return 0; 289 } 290 synchronized (mLock) { 291 long duration = mNativeWrapper.compose(primitives, vibrationId); 292 if (duration > 0) { 293 mCurrentAmplitude = -1; 294 notifyListenerOnVibrating(true); 295 } 296 return duration; 297 } 298 } 299 300 /** 301 * Plays a composition of pwle primitives, using {@code vibrationId} or completion callback 302 * to {@link OnVibrationCompleteListener}. 303 * 304 * <p>This will affect the state of {@link #isVibrating()}. 305 * 306 * @return The duration of the effect playing, or 0 if unsupported. 307 */ on(RampSegment[] primitives, long vibrationId)308 public long on(RampSegment[] primitives, long vibrationId) { 309 if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) { 310 return 0; 311 } 312 synchronized (mLock) { 313 int braking = mVibratorInfo.getDefaultBraking(); 314 long duration = mNativeWrapper.composePwle(primitives, braking, vibrationId); 315 if (duration > 0) { 316 mCurrentAmplitude = -1; 317 notifyListenerOnVibrating(true); 318 } 319 return duration; 320 } 321 } 322 323 /** Turns off the vibrator. This will affect the state of {@link #isVibrating()}. */ off()324 public void off() { 325 synchronized (mLock) { 326 mNativeWrapper.off(); 327 mCurrentAmplitude = 0; 328 notifyListenerOnVibrating(false); 329 } 330 } 331 332 /** 333 * Resets the vibrator hardware to a default state. 334 * This turns the vibrator off, which will affect the state of {@link #isVibrating()}. 335 */ reset()336 public void reset() { 337 setExternalControl(false); 338 off(); 339 } 340 341 @Override toString()342 public String toString() { 343 return "VibratorController{" 344 + "mVibratorInfo=" + mVibratorInfo 345 + ", mVibratorInfoLoadSuccessful=" + mVibratorInfoLoadSuccessful 346 + ", mIsVibrating=" + mIsVibrating 347 + ", mCurrentAmplitude=" + mCurrentAmplitude 348 + ", mIsUnderExternalControl=" + mIsUnderExternalControl 349 + ", mVibratorStateListeners count=" 350 + mVibratorStateListeners.getRegisteredCallbackCount() 351 + '}'; 352 } 353 354 @GuardedBy("mLock") notifyListenerOnVibrating(boolean isVibrating)355 private void notifyListenerOnVibrating(boolean isVibrating) { 356 if (mIsVibrating != isVibrating) { 357 mIsVibrating = isVibrating; 358 // The broadcast method is safe w.r.t. register/unregister listener methods, but lock 359 // is required here to guarantee delivery order. 360 mVibratorStateListeners.broadcast( 361 listener -> notifyStateListener(listener, isVibrating)); 362 } 363 } 364 notifyStateListener(IVibratorStateListener listener, boolean isVibrating)365 private void notifyStateListener(IVibratorStateListener listener, boolean isVibrating) { 366 try { 367 listener.onVibrating(isVibrating); 368 } catch (RemoteException | RuntimeException e) { 369 Slog.e(TAG, "Vibrator state listener failed to call", e); 370 } 371 } 372 373 /** Wrapper around the static-native methods of {@link VibratorController} for tests. */ 374 @VisibleForTesting 375 public static class NativeWrapper { 376 /** 377 * Initializes the native part of this controller, creating a global reference to given 378 * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This 379 * wrapper is responsible for deleting this pointer by calling the method pointed 380 * by {@link #getNativeFinalizer()}. 381 * 382 * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener} 383 * do not hold any strong reference to the instance responsible for deleting the returned 384 * pointer, to avoid creating a cyclic GC root reference. 385 */ nativeInit(int vibratorId, OnVibrationCompleteListener listener)386 private static native long nativeInit(int vibratorId, OnVibrationCompleteListener listener); 387 388 /** 389 * Returns pointer to native function responsible for cleaning up the native pointer 390 * allocated and returned by {@link #nativeInit(int, OnVibrationCompleteListener)}. 391 */ getNativeFinalizer()392 private static native long getNativeFinalizer(); 393 isAvailable(long nativePtr)394 private static native boolean isAvailable(long nativePtr); 395 on(long nativePtr, long milliseconds, long vibrationId)396 private static native long on(long nativePtr, long milliseconds, long vibrationId); 397 off(long nativePtr)398 private static native void off(long nativePtr); 399 setAmplitude(long nativePtr, float amplitude)400 private static native void setAmplitude(long nativePtr, float amplitude); 401 performEffect(long nativePtr, long effect, long strength, long vibrationId)402 private static native long performEffect(long nativePtr, long effect, long strength, 403 long vibrationId); 404 performComposedEffect(long nativePtr, PrimitiveSegment[] effect, long vibrationId)405 private static native long performComposedEffect(long nativePtr, PrimitiveSegment[] effect, 406 long vibrationId); 407 performPwleEffect(long nativePtr, RampSegment[] effect, int braking, long vibrationId)408 private static native long performPwleEffect(long nativePtr, RampSegment[] effect, 409 int braking, long vibrationId); 410 setExternalControl(long nativePtr, boolean enabled)411 private static native void setExternalControl(long nativePtr, boolean enabled); 412 alwaysOnEnable(long nativePtr, long id, long effect, long strength)413 private static native void alwaysOnEnable(long nativePtr, long id, long effect, 414 long strength); 415 alwaysOnDisable(long nativePtr, long id)416 private static native void alwaysOnDisable(long nativePtr, long id); 417 getInfo(long nativePtr, VibratorInfo.Builder infoBuilder)418 private static native boolean getInfo(long nativePtr, VibratorInfo.Builder infoBuilder); 419 420 private long mNativePtr = 0; 421 422 /** Initializes native controller and allocation registry to destroy native instances. */ init(int vibratorId, OnVibrationCompleteListener listener)423 public void init(int vibratorId, OnVibrationCompleteListener listener) { 424 mNativePtr = nativeInit(vibratorId, listener); 425 long finalizerPtr = getNativeFinalizer(); 426 427 if (finalizerPtr != 0) { 428 NativeAllocationRegistry registry = 429 NativeAllocationRegistry.createMalloced( 430 VibratorController.class.getClassLoader(), finalizerPtr); 431 registry.registerNativeAllocation(this, mNativePtr); 432 } 433 } 434 435 /** Check if the vibrator is currently available. */ isAvailable()436 public boolean isAvailable() { 437 return isAvailable(mNativePtr); 438 } 439 440 /** Turns vibrator on for given time. */ on(long milliseconds, long vibrationId)441 public long on(long milliseconds, long vibrationId) { 442 return on(mNativePtr, milliseconds, vibrationId); 443 } 444 445 /** Turns vibrator off. */ off()446 public void off() { 447 off(mNativePtr); 448 } 449 450 /** Sets the amplitude for the vibrator to run. */ setAmplitude(float amplitude)451 public void setAmplitude(float amplitude) { 452 setAmplitude(mNativePtr, amplitude); 453 } 454 455 /** Turns vibrator on to perform one of the supported effects. */ perform(long effect, long strength, long vibrationId)456 public long perform(long effect, long strength, long vibrationId) { 457 return performEffect(mNativePtr, effect, strength, vibrationId); 458 } 459 460 /** Turns vibrator on to perform effect composed of give primitives effect. */ compose(PrimitiveSegment[] primitives, long vibrationId)461 public long compose(PrimitiveSegment[] primitives, long vibrationId) { 462 return performComposedEffect(mNativePtr, primitives, vibrationId); 463 } 464 465 /** Turns vibrator on to perform PWLE effect composed of given primitives. */ composePwle(RampSegment[] primitives, int braking, long vibrationId)466 public long composePwle(RampSegment[] primitives, int braking, long vibrationId) { 467 return performPwleEffect(mNativePtr, primitives, braking, vibrationId); 468 } 469 470 /** Enabled the device vibrator to be controlled by another service. */ setExternalControl(boolean enabled)471 public void setExternalControl(boolean enabled) { 472 setExternalControl(mNativePtr, enabled); 473 } 474 475 /** Enable always-on vibration with given id and effect. */ alwaysOnEnable(long id, long effect, long strength)476 public void alwaysOnEnable(long id, long effect, long strength) { 477 alwaysOnEnable(mNativePtr, id, effect, strength); 478 } 479 480 /** Disable always-on vibration for given id. */ alwaysOnDisable(long id)481 public void alwaysOnDisable(long id) { 482 alwaysOnDisable(mNativePtr, id); 483 } 484 485 /** 486 * Loads device vibrator metadata and returns true if all metadata was loaded successfully. 487 */ getInfo(VibratorInfo.Builder infoBuilder)488 public boolean getInfo(VibratorInfo.Builder infoBuilder) { 489 return getInfo(mNativePtr, infoBuilder); 490 } 491 } 492 } 493