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