1 /* 2 * Copyright (C) 2012 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 android.os; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.compat.annotation.UnsupportedAppUsage; 23 import android.content.Context; 24 import android.hardware.vibrator.IVibratorManager; 25 import android.os.vibrator.VendorVibrationSession; 26 import android.os.vibrator.VibratorInfoFactory; 27 import android.util.ArrayMap; 28 import android.util.Log; 29 import android.util.SparseArray; 30 31 import com.android.internal.annotations.GuardedBy; 32 import com.android.internal.annotations.VisibleForTesting; 33 34 import java.util.ArrayList; 35 import java.util.Objects; 36 import java.util.concurrent.Executor; 37 38 /** 39 * Vibrator implementation that controls the main system vibrator. 40 * 41 * @hide 42 */ 43 public class SystemVibrator extends Vibrator { 44 private static final String TAG = "Vibrator"; 45 46 private final VibratorManager mVibratorManager; 47 private final Context mContext; 48 49 @GuardedBy("mBrokenListeners") 50 private final ArrayList<MultiVibratorStateListener> mBrokenListeners = new ArrayList<>(); 51 52 @GuardedBy("mRegisteredListeners") 53 private final ArrayMap<OnVibratorStateChangedListener, MultiVibratorStateListener> 54 mRegisteredListeners = new ArrayMap<>(); 55 56 private final Object mLock = new Object(); 57 @GuardedBy("mLock") 58 private VibratorInfo mVibratorInfo; 59 private int[] mVibratorIds; 60 61 @UnsupportedAppUsage SystemVibrator(Context context)62 public SystemVibrator(Context context) { 63 super(context); 64 mContext = context; 65 mVibratorManager = mContext.getSystemService(VibratorManager.class); 66 } 67 68 @Override getInfo()69 public VibratorInfo getInfo() { 70 synchronized (mLock) { 71 if (mVibratorInfo != null) { 72 return mVibratorInfo; 73 } 74 if (mVibratorManager == null) { 75 Log.w(TAG, "Failed to retrieve vibrator info; no vibrator manager."); 76 return VibratorInfo.EMPTY_VIBRATOR_INFO; 77 } 78 int[] vibratorIds = getVibratorIds(); 79 if (vibratorIds == null) { 80 Log.w(TAG, "Failed to retrieve vibrator info; error retrieving vibrator ids."); 81 return VibratorInfo.EMPTY_VIBRATOR_INFO; 82 } 83 if (vibratorIds.length == 0) { 84 // It is known that the device has no vibrator, so cache and return info that 85 // reflects the lack of support for effects/primitives. 86 return mVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO; 87 } 88 VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length]; 89 for (int i = 0; i < vibratorIds.length; i++) { 90 Vibrator vibrator = mVibratorManager.getVibrator(vibratorIds[i]); 91 if (vibrator instanceof NullVibrator) { 92 Log.w(TAG, "Vibrator manager service not ready; " 93 + "Info not yet available for vibrator: " + vibratorIds[i]); 94 // This should never happen after the vibrator manager service is ready. 95 // Skip caching this vibrator until then. 96 return VibratorInfo.EMPTY_VIBRATOR_INFO; 97 } 98 vibratorInfos[i] = vibrator.getInfo(); 99 } 100 return mVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, vibratorInfos); 101 } 102 } 103 104 @Override hasVibrator()105 public boolean hasVibrator() { 106 int[] vibratorIds = getVibratorIds(); 107 if (vibratorIds == null) { 108 Log.w(TAG, "Failed to check if vibrator exists; no vibrator manager."); 109 return false; 110 } 111 return vibratorIds.length > 0; 112 } 113 114 @Override isVibrating()115 public boolean isVibrating() { 116 int[] vibratorIds = getVibratorIds(); 117 if (vibratorIds == null) { 118 Log.w(TAG, "Failed to vibrate; no vibrator manager."); 119 return false; 120 } 121 for (int vibratorId : vibratorIds) { 122 if (mVibratorManager.getVibrator(vibratorId).isVibrating()) { 123 return true; 124 } 125 } 126 return false; 127 } 128 129 @Override addVibratorStateListener(@onNull OnVibratorStateChangedListener listener)130 public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { 131 Objects.requireNonNull(listener); 132 if (mContext == null) { 133 Log.w(TAG, "Failed to add vibrate state listener; no vibrator context."); 134 return; 135 } 136 addVibratorStateListener(mContext.getMainExecutor(), listener); 137 } 138 139 @Override addVibratorStateListener( @onNull @allbackExecutor Executor executor, @NonNull OnVibratorStateChangedListener listener)140 public void addVibratorStateListener( 141 @NonNull @CallbackExecutor Executor executor, 142 @NonNull OnVibratorStateChangedListener listener) { 143 Objects.requireNonNull(listener); 144 Objects.requireNonNull(executor); 145 if (mVibratorManager == null) { 146 Log.w(TAG, "Failed to add vibrate state listener; no vibrator manager."); 147 return; 148 } 149 int[] vibratorIds = getVibratorIds(); 150 if (vibratorIds == null) { 151 Log.w(TAG, "Failed to add vibrate state listener; error retrieving vibrator ids."); 152 return; 153 } 154 MultiVibratorStateListener delegate = null; 155 try { 156 synchronized (mRegisteredListeners) { 157 // If listener is already registered, reject and return. 158 if (mRegisteredListeners.containsKey(listener)) { 159 Log.w(TAG, "Listener already registered."); 160 return; 161 } 162 delegate = new MultiVibratorStateListener(executor, listener); 163 delegate.register(mVibratorManager, vibratorIds); 164 mRegisteredListeners.put(listener, delegate); 165 delegate = null; 166 } 167 } finally { 168 if (delegate != null && delegate.hasRegisteredListeners()) { 169 // The delegate listener was left in a partial state with listeners registered to 170 // some but not all vibrators. Keep track of this to try to unregister them later. 171 synchronized (mBrokenListeners) { 172 mBrokenListeners.add(delegate); 173 } 174 } 175 tryUnregisterBrokenListeners(); 176 } 177 } 178 179 @Override removeVibratorStateListener(@onNull OnVibratorStateChangedListener listener)180 public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { 181 Objects.requireNonNull(listener); 182 if (mVibratorManager == null) { 183 Log.w(TAG, "Failed to remove vibrate state listener; no vibrator manager."); 184 return; 185 } 186 synchronized (mRegisteredListeners) { 187 if (mRegisteredListeners.containsKey(listener)) { 188 MultiVibratorStateListener delegate = mRegisteredListeners.get(listener); 189 delegate.unregister(mVibratorManager); 190 mRegisteredListeners.remove(listener); 191 } 192 } 193 tryUnregisterBrokenListeners(); 194 } 195 196 @Override hasAmplitudeControl()197 public boolean hasAmplitudeControl() { 198 return getInfo().hasAmplitudeControl(); 199 } 200 201 @Override areVendorSessionsSupported()202 public boolean areVendorSessionsSupported() { 203 return mVibratorManager.hasCapabilities(IVibratorManager.CAP_START_SESSIONS); 204 } 205 206 @Override setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect, VibrationAttributes attrs)207 public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect, 208 VibrationAttributes attrs) { 209 if (mVibratorManager == null) { 210 Log.w(TAG, "Failed to set always-on effect; no vibrator manager."); 211 return false; 212 } 213 CombinedVibration combinedEffect = CombinedVibration.createParallel(effect); 214 return mVibratorManager.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combinedEffect, attrs); 215 } 216 217 @Override vibrate(int uid, String opPkg, @NonNull VibrationEffect effect, String reason, @NonNull VibrationAttributes attributes)218 public void vibrate(int uid, String opPkg, @NonNull VibrationEffect effect, 219 String reason, @NonNull VibrationAttributes attributes) { 220 if (mVibratorManager == null) { 221 Log.w(TAG, "Failed to vibrate; no vibrator manager."); 222 return; 223 } 224 CombinedVibration combinedEffect = CombinedVibration.createParallel(effect); 225 mVibratorManager.vibrate(uid, opPkg, combinedEffect, reason, attributes); 226 } 227 228 @Override performHapticFeedback(int constant, String reason, int flags, int privFlags)229 public void performHapticFeedback(int constant, String reason, int flags, int privFlags) { 230 if (mVibratorManager == null) { 231 Log.w(TAG, "Failed to perform haptic feedback; no vibrator manager."); 232 return; 233 } 234 mVibratorManager.performHapticFeedback(constant, reason, flags, privFlags); 235 } 236 237 @Override performHapticFeedbackForInputDevice(int constant, int inputDeviceId, int inputSource, String reason, int flags, int privFlags)238 public void performHapticFeedbackForInputDevice(int constant, int inputDeviceId, 239 int inputSource, String reason, int flags, int privFlags) { 240 if (mVibratorManager == null) { 241 Log.w(TAG, "Failed to perform haptic feedback for input device; no vibrator manager."); 242 return; 243 } 244 mVibratorManager.performHapticFeedbackForInputDevice(constant, inputDeviceId, inputSource, 245 reason, flags, privFlags); 246 } 247 248 @Override cancel()249 public void cancel() { 250 if (mVibratorManager == null) { 251 Log.w(TAG, "Failed to cancel vibrate; no vibrator manager."); 252 return; 253 } 254 mVibratorManager.cancel(); 255 } 256 257 @Override cancel(int usageFilter)258 public void cancel(int usageFilter) { 259 if (mVibratorManager == null) { 260 Log.w(TAG, "Failed to cancel vibrate; no vibrator manager."); 261 return; 262 } 263 mVibratorManager.cancel(usageFilter); 264 } 265 266 @Override startVendorSession(@onNull VibrationAttributes attrs, @Nullable String reason, @Nullable CancellationSignal cancellationSignal, @NonNull Executor executor, @NonNull VendorVibrationSession.Callback callback)267 public void startVendorSession(@NonNull VibrationAttributes attrs, @Nullable String reason, 268 @Nullable CancellationSignal cancellationSignal, @NonNull Executor executor, 269 @NonNull VendorVibrationSession.Callback callback) { 270 if (mVibratorManager == null) { 271 Log.w(TAG, "Failed to start vibration session; no vibrator manager."); 272 executor.execute( 273 () -> callback.onFinished(VendorVibrationSession.STATUS_UNKNOWN_ERROR)); 274 return; 275 } 276 int[] vibratorIds = getVibratorIds(); 277 if (vibratorIds == null) { 278 Log.w(TAG, "Failed to start vibration session; error retrieving vibrator ids."); 279 executor.execute( 280 () -> callback.onFinished(VendorVibrationSession.STATUS_UNKNOWN_ERROR)); 281 return; 282 } 283 mVibratorManager.startVendorSession(vibratorIds, attrs, reason, cancellationSignal, 284 executor, callback); 285 } 286 287 @Nullable getVibratorIds()288 private int[] getVibratorIds() { 289 synchronized (mLock) { 290 if (mVibratorIds != null) { 291 return mVibratorIds; 292 } 293 if (mVibratorManager == null) { 294 Log.w(TAG, "Failed to retrieve vibrator ids; no vibrator manager."); 295 return null; 296 } 297 return mVibratorIds = mVibratorManager.getVibratorIds(); 298 } 299 } 300 301 /** 302 * Tries to unregister individual {@link android.os.Vibrator.OnVibratorStateChangedListener} 303 * that were left registered to vibrators after failures to register them to all vibrators. 304 * 305 * <p>This might happen if {@link MultiVibratorStateListener} fails to register to any vibrator 306 * and also fails to unregister any previously registered single listeners to other vibrators. 307 * 308 * <p>This method never throws {@link RuntimeException} if it fails to unregister again, it will 309 * fail silently and attempt to unregister the same broken listener later. 310 */ tryUnregisterBrokenListeners()311 private void tryUnregisterBrokenListeners() { 312 synchronized (mBrokenListeners) { 313 try { 314 for (int i = mBrokenListeners.size(); --i >= 0; ) { 315 mBrokenListeners.get(i).unregister(mVibratorManager); 316 mBrokenListeners.remove(i); 317 } 318 } catch (RuntimeException e) { 319 Log.w(TAG, "Failed to unregister broken listener", e); 320 } 321 } 322 } 323 324 /** Listener for a single vibrator state change. */ 325 private static class SingleVibratorStateListener implements OnVibratorStateChangedListener { 326 private final MultiVibratorStateListener mAllVibratorsListener; 327 private final int mVibratorIdx; 328 SingleVibratorStateListener(MultiVibratorStateListener listener, int vibratorIdx)329 SingleVibratorStateListener(MultiVibratorStateListener listener, int vibratorIdx) { 330 mAllVibratorsListener = listener; 331 mVibratorIdx = vibratorIdx; 332 } 333 334 @Override onVibratorStateChanged(boolean isVibrating)335 public void onVibratorStateChanged(boolean isVibrating) { 336 mAllVibratorsListener.onVibrating(mVibratorIdx, isVibrating); 337 } 338 } 339 340 /** 341 * Listener for all vibrators state change. 342 * 343 * <p>This registers a listener to all vibrators to merge the callbacks into a single state 344 * that is set to true if any individual vibrator is also true, and false otherwise. 345 * 346 * @hide 347 */ 348 @VisibleForTesting 349 public static class MultiVibratorStateListener { 350 private final Object mLock = new Object(); 351 private final Executor mExecutor; 352 private final OnVibratorStateChangedListener mDelegate; 353 354 @GuardedBy("mLock") 355 private final SparseArray<SingleVibratorStateListener> mVibratorListeners = 356 new SparseArray<>(); 357 358 @GuardedBy("mLock") 359 private int mInitializedMask; 360 @GuardedBy("mLock") 361 private int mVibratingMask; 362 MultiVibratorStateListener(@onNull Executor executor, @NonNull OnVibratorStateChangedListener listener)363 public MultiVibratorStateListener(@NonNull Executor executor, 364 @NonNull OnVibratorStateChangedListener listener) { 365 mExecutor = executor; 366 mDelegate = listener; 367 } 368 369 /** Returns true if at least one listener was registered to an individual vibrator. */ hasRegisteredListeners()370 public boolean hasRegisteredListeners() { 371 synchronized (mLock) { 372 return mVibratorListeners.size() > 0; 373 } 374 } 375 376 /** Registers a listener to all individual vibrators in {@link VibratorManager}. */ register(VibratorManager vibratorManager, @NonNull int[] vibratorIds)377 public void register(VibratorManager vibratorManager, @NonNull int[] vibratorIds) { 378 synchronized (mLock) { 379 for (int i = 0; i < vibratorIds.length; i++) { 380 int vibratorId = vibratorIds[i]; 381 SingleVibratorStateListener listener = new SingleVibratorStateListener(this, i); 382 try { 383 vibratorManager.getVibrator(vibratorId).addVibratorStateListener(mExecutor, 384 listener); 385 mVibratorListeners.put(vibratorId, listener); 386 } catch (RuntimeException e) { 387 try { 388 unregister(vibratorManager); 389 } catch (RuntimeException e1) { 390 Log.w(TAG, 391 "Failed to unregister listener while recovering from a failed " 392 + "register call", e1); 393 } 394 throw e; 395 } 396 } 397 } 398 } 399 400 /** Unregisters the listeners from all individual vibrators in {@link VibratorManager}. */ unregister(VibratorManager vibratorManager)401 public void unregister(VibratorManager vibratorManager) { 402 synchronized (mLock) { 403 for (int i = mVibratorListeners.size(); --i >= 0; ) { 404 int vibratorId = mVibratorListeners.keyAt(i); 405 SingleVibratorStateListener listener = mVibratorListeners.valueAt(i); 406 vibratorManager.getVibrator(vibratorId).removeVibratorStateListener(listener); 407 mVibratorListeners.removeAt(i); 408 } 409 } 410 } 411 412 /** Callback triggered by {@link SingleVibratorStateListener} for each vibrator. */ onVibrating(int vibratorIdx, boolean vibrating)413 public void onVibrating(int vibratorIdx, boolean vibrating) { 414 mExecutor.execute(() -> { 415 boolean shouldNotifyStateChange; 416 boolean isAnyVibrating; 417 synchronized (mLock) { 418 // Bitmask indicating that all vibrators have been initialized. 419 int allInitializedMask = (1 << mVibratorListeners.size()) - 1; 420 421 // Save current global state before processing this vibrator state change. 422 boolean previousIsAnyVibrating = (mVibratingMask != 0); 423 boolean previousAreAllInitialized = (mInitializedMask == allInitializedMask); 424 425 // Mark this vibrator as initialized. 426 int vibratorMask = (1 << vibratorIdx); 427 mInitializedMask |= vibratorMask; 428 429 // Flip the vibrating bit flag for this vibrator, only if the state is changing. 430 boolean previousVibrating = (mVibratingMask & vibratorMask) != 0; 431 if (previousVibrating != vibrating) { 432 mVibratingMask ^= vibratorMask; 433 } 434 435 // Check new global state after processing this vibrator state change. 436 isAnyVibrating = (mVibratingMask != 0); 437 boolean areAllInitialized = (mInitializedMask == allInitializedMask); 438 439 // Prevent multiple triggers with the same state. 440 // Trigger once when all vibrators have reported their state, and then only when 441 // the merged vibrating state changes. 442 boolean isStateChanging = (previousIsAnyVibrating != isAnyVibrating); 443 shouldNotifyStateChange = 444 areAllInitialized && (!previousAreAllInitialized || isStateChanging); 445 } 446 // Notify delegate listener outside the lock, only if merged state is changing. 447 if (shouldNotifyStateChange) { 448 mDelegate.onVibratorStateChanged(isAnyVibrating); 449 } 450 }); 451 } 452 } 453 } 454