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