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.compat.annotation.UnsupportedAppUsage; 22 import android.content.Context; 23 import android.media.AudioAttributes; 24 import android.util.ArrayMap; 25 import android.util.Log; 26 import android.util.SparseArray; 27 28 import com.android.internal.annotations.GuardedBy; 29 import com.android.internal.annotations.VisibleForTesting; 30 31 import java.util.ArrayList; 32 import java.util.Objects; 33 import java.util.concurrent.Executor; 34 35 /** 36 * Vibrator implementation that controls the main system vibrator. 37 * 38 * @hide 39 */ 40 public class SystemVibrator extends Vibrator { 41 private static final String TAG = "Vibrator"; 42 43 private final VibratorManager mVibratorManager; 44 private final Context mContext; 45 46 @GuardedBy("mBrokenListeners") 47 private final ArrayList<AllVibratorsStateListener> mBrokenListeners = new ArrayList<>(); 48 49 @GuardedBy("mRegisteredListeners") 50 private final ArrayMap<OnVibratorStateChangedListener, AllVibratorsStateListener> 51 mRegisteredListeners = new ArrayMap<>(); 52 53 private final Object mLock = new Object(); 54 private AllVibratorsInfo mVibratorInfo; 55 56 @UnsupportedAppUsage SystemVibrator(Context context)57 public SystemVibrator(Context context) { 58 super(context); 59 mContext = context; 60 mVibratorManager = mContext.getSystemService(VibratorManager.class); 61 } 62 63 @Override getInfo()64 protected VibratorInfo getInfo() { 65 synchronized (mLock) { 66 if (mVibratorInfo != null) { 67 return mVibratorInfo; 68 } 69 if (mVibratorManager == null) { 70 Log.w(TAG, "Failed to retrieve vibrator info; no vibrator manager."); 71 return VibratorInfo.EMPTY_VIBRATOR_INFO; 72 } 73 int[] vibratorIds = mVibratorManager.getVibratorIds(); 74 VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length]; 75 for (int i = 0; i < vibratorIds.length; i++) { 76 vibratorInfos[i] = mVibratorManager.getVibrator(vibratorIds[i]).getInfo(); 77 } 78 return mVibratorInfo = new AllVibratorsInfo(vibratorInfos); 79 } 80 } 81 82 @Override hasVibrator()83 public boolean hasVibrator() { 84 if (mVibratorManager == null) { 85 Log.w(TAG, "Failed to check if vibrator exists; no vibrator manager."); 86 return false; 87 } 88 return mVibratorManager.getVibratorIds().length > 0; 89 } 90 91 @Override isVibrating()92 public boolean isVibrating() { 93 if (mVibratorManager == null) { 94 Log.w(TAG, "Failed to vibrate; no vibrator manager."); 95 return false; 96 } 97 for (int vibratorId : mVibratorManager.getVibratorIds()) { 98 if (mVibratorManager.getVibrator(vibratorId).isVibrating()) { 99 return true; 100 } 101 } 102 return false; 103 } 104 105 @Override addVibratorStateListener(@onNull OnVibratorStateChangedListener listener)106 public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { 107 Objects.requireNonNull(listener); 108 if (mContext == null) { 109 Log.w(TAG, "Failed to add vibrate state listener; no vibrator context."); 110 return; 111 } 112 addVibratorStateListener(mContext.getMainExecutor(), listener); 113 } 114 115 @Override addVibratorStateListener( @onNull @allbackExecutor Executor executor, @NonNull OnVibratorStateChangedListener listener)116 public void addVibratorStateListener( 117 @NonNull @CallbackExecutor Executor executor, 118 @NonNull OnVibratorStateChangedListener listener) { 119 Objects.requireNonNull(listener); 120 Objects.requireNonNull(executor); 121 if (mVibratorManager == null) { 122 Log.w(TAG, "Failed to add vibrate state listener; no vibrator manager."); 123 return; 124 } 125 AllVibratorsStateListener delegate = null; 126 try { 127 synchronized (mRegisteredListeners) { 128 // If listener is already registered, reject and return. 129 if (mRegisteredListeners.containsKey(listener)) { 130 Log.w(TAG, "Listener already registered."); 131 return; 132 } 133 delegate = new AllVibratorsStateListener(executor, listener); 134 delegate.register(mVibratorManager); 135 mRegisteredListeners.put(listener, delegate); 136 delegate = null; 137 } 138 } finally { 139 if (delegate != null && delegate.hasRegisteredListeners()) { 140 // The delegate listener was left in a partial state with listeners registered to 141 // some but not all vibrators. Keep track of this to try to unregister them later. 142 synchronized (mBrokenListeners) { 143 mBrokenListeners.add(delegate); 144 } 145 } 146 tryUnregisterBrokenListeners(); 147 } 148 } 149 150 @Override removeVibratorStateListener(@onNull OnVibratorStateChangedListener listener)151 public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { 152 Objects.requireNonNull(listener); 153 if (mVibratorManager == null) { 154 Log.w(TAG, "Failed to remove vibrate state listener; no vibrator manager."); 155 return; 156 } 157 synchronized (mRegisteredListeners) { 158 if (mRegisteredListeners.containsKey(listener)) { 159 AllVibratorsStateListener delegate = mRegisteredListeners.get(listener); 160 delegate.unregister(mVibratorManager); 161 mRegisteredListeners.remove(listener); 162 } 163 } 164 tryUnregisterBrokenListeners(); 165 } 166 167 @Override hasAmplitudeControl()168 public boolean hasAmplitudeControl() { 169 return getInfo().hasAmplitudeControl(); 170 } 171 172 @Override setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect, AudioAttributes attributes)173 public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect, 174 AudioAttributes attributes) { 175 if (mVibratorManager == null) { 176 Log.w(TAG, "Failed to set always-on effect; no vibrator manager."); 177 return false; 178 } 179 VibrationAttributes attr = new VibrationAttributes.Builder(attributes, effect).build(); 180 CombinedVibration combinedEffect = CombinedVibration.createParallel(effect); 181 return mVibratorManager.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combinedEffect, attr); 182 } 183 184 @Override vibrate(int uid, String opPkg, @NonNull VibrationEffect effect, String reason, @NonNull VibrationAttributes attributes)185 public void vibrate(int uid, String opPkg, @NonNull VibrationEffect effect, 186 String reason, @NonNull VibrationAttributes attributes) { 187 if (mVibratorManager == null) { 188 Log.w(TAG, "Failed to vibrate; no vibrator manager."); 189 return; 190 } 191 CombinedVibration combinedEffect = CombinedVibration.createParallel(effect); 192 mVibratorManager.vibrate(uid, opPkg, combinedEffect, reason, attributes); 193 } 194 195 @Override cancel()196 public void cancel() { 197 if (mVibratorManager == null) { 198 Log.w(TAG, "Failed to cancel vibrate; no vibrator manager."); 199 return; 200 } 201 mVibratorManager.cancel(); 202 } 203 204 @Override cancel(int usageFilter)205 public void cancel(int usageFilter) { 206 if (mVibratorManager == null) { 207 Log.w(TAG, "Failed to cancel vibrate; no vibrator manager."); 208 return; 209 } 210 mVibratorManager.cancel(usageFilter); 211 } 212 213 /** 214 * Tries to unregister individual {@link android.os.Vibrator.OnVibratorStateChangedListener} 215 * that were left registered to vibrators after failures to register them to all vibrators. 216 * 217 * <p>This might happen if {@link AllVibratorsStateListener} fails to register to any vibrator 218 * and also fails to unregister any previously registered single listeners to other vibrators. 219 * 220 * <p>This method never throws {@link RuntimeException} if it fails to unregister again, it will 221 * fail silently and attempt to unregister the same broken listener later. 222 */ tryUnregisterBrokenListeners()223 private void tryUnregisterBrokenListeners() { 224 synchronized (mBrokenListeners) { 225 try { 226 for (int i = mBrokenListeners.size(); --i >= 0; ) { 227 mBrokenListeners.get(i).unregister(mVibratorManager); 228 mBrokenListeners.remove(i); 229 } 230 } catch (RuntimeException e) { 231 Log.w(TAG, "Failed to unregister broken listener", e); 232 } 233 } 234 } 235 236 /** Listener for a single vibrator state change. */ 237 private static class SingleVibratorStateListener implements OnVibratorStateChangedListener { 238 private final AllVibratorsStateListener mAllVibratorsListener; 239 private final int mVibratorIdx; 240 SingleVibratorStateListener(AllVibratorsStateListener listener, int vibratorIdx)241 SingleVibratorStateListener(AllVibratorsStateListener listener, int vibratorIdx) { 242 mAllVibratorsListener = listener; 243 mVibratorIdx = vibratorIdx; 244 } 245 246 @Override onVibratorStateChanged(boolean isVibrating)247 public void onVibratorStateChanged(boolean isVibrating) { 248 mAllVibratorsListener.onVibrating(mVibratorIdx, isVibrating); 249 } 250 } 251 252 /** 253 * Represents all the vibrators information as a single {@link VibratorInfo}. 254 * 255 * <p>This uses the first vibrator on the list as the default one for all hardware spec, but 256 * uses an intersection of all vibrators to decide the capabilities and effect/primitive 257 * support. 258 * 259 * @hide 260 */ 261 @VisibleForTesting 262 public static class AllVibratorsInfo extends VibratorInfo { 263 private final VibratorInfo[] mVibratorInfos; 264 AllVibratorsInfo(VibratorInfo[] vibrators)265 public AllVibratorsInfo(VibratorInfo[] vibrators) { 266 super(/* id= */ -1, capabilitiesIntersection(vibrators), 267 vibrators.length > 0 ? vibrators[0] : VibratorInfo.EMPTY_VIBRATOR_INFO); 268 mVibratorInfos = vibrators; 269 } 270 271 @Override isEffectSupported(int effectId)272 public int isEffectSupported(int effectId) { 273 if (mVibratorInfos.length == 0) { 274 return Vibrator.VIBRATION_EFFECT_SUPPORT_NO; 275 } 276 int supported = Vibrator.VIBRATION_EFFECT_SUPPORT_YES; 277 for (VibratorInfo info : mVibratorInfos) { 278 int effectSupported = info.isEffectSupported(effectId); 279 if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_NO) { 280 return effectSupported; 281 } else if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN) { 282 supported = effectSupported; 283 } 284 } 285 return supported; 286 } 287 288 @Override isPrimitiveSupported(int primitiveId)289 public boolean isPrimitiveSupported(int primitiveId) { 290 if (mVibratorInfos.length == 0) { 291 return false; 292 } 293 for (VibratorInfo info : mVibratorInfos) { 294 if (!info.isPrimitiveSupported(primitiveId)) { 295 return false; 296 } 297 } 298 return true; 299 } 300 301 @Override getPrimitiveDuration(int primitiveId)302 public int getPrimitiveDuration(int primitiveId) { 303 int maxDuration = 0; 304 for (VibratorInfo info : mVibratorInfos) { 305 int duration = info.getPrimitiveDuration(primitiveId); 306 if (duration == 0) { 307 return 0; 308 } 309 maxDuration = Math.max(maxDuration, duration); 310 } 311 return maxDuration; 312 } 313 capabilitiesIntersection(VibratorInfo[] infos)314 private static int capabilitiesIntersection(VibratorInfo[] infos) { 315 if (infos.length == 0) { 316 return 0; 317 } 318 int intersection = ~0; 319 for (VibratorInfo info : infos) { 320 intersection &= info.getCapabilities(); 321 } 322 return intersection; 323 } 324 } 325 326 /** Listener for all vibrators state change. */ 327 private static class AllVibratorsStateListener { 328 private final Object mLock = new Object(); 329 private final Executor mExecutor; 330 private final OnVibratorStateChangedListener mDelegate; 331 332 @GuardedBy("mLock") 333 private final SparseArray<SingleVibratorStateListener> mVibratorListeners = 334 new SparseArray<>(); 335 336 @GuardedBy("mLock") 337 private int mInitializedMask; 338 @GuardedBy("mLock") 339 private int mVibratingMask; 340 AllVibratorsStateListener(@onNull Executor executor, @NonNull OnVibratorStateChangedListener listener)341 AllVibratorsStateListener(@NonNull Executor executor, 342 @NonNull OnVibratorStateChangedListener listener) { 343 mExecutor = executor; 344 mDelegate = listener; 345 } 346 hasRegisteredListeners()347 boolean hasRegisteredListeners() { 348 synchronized (mLock) { 349 return mVibratorListeners.size() > 0; 350 } 351 } 352 register(VibratorManager vibratorManager)353 void register(VibratorManager vibratorManager) { 354 int[] vibratorIds = vibratorManager.getVibratorIds(); 355 synchronized (mLock) { 356 for (int i = 0; i < vibratorIds.length; i++) { 357 int vibratorId = vibratorIds[i]; 358 SingleVibratorStateListener listener = new SingleVibratorStateListener(this, i); 359 try { 360 vibratorManager.getVibrator(vibratorId).addVibratorStateListener(mExecutor, 361 listener); 362 mVibratorListeners.put(vibratorId, listener); 363 } catch (RuntimeException e) { 364 try { 365 unregister(vibratorManager); 366 } catch (RuntimeException e1) { 367 Log.w(TAG, 368 "Failed to unregister listener while recovering from a failed " 369 + "register call", e1); 370 } 371 throw e; 372 } 373 } 374 } 375 } 376 unregister(VibratorManager vibratorManager)377 void unregister(VibratorManager vibratorManager) { 378 synchronized (mLock) { 379 for (int i = mVibratorListeners.size(); --i >= 0; ) { 380 int vibratorId = mVibratorListeners.keyAt(i); 381 SingleVibratorStateListener listener = mVibratorListeners.valueAt(i); 382 vibratorManager.getVibrator(vibratorId).removeVibratorStateListener(listener); 383 mVibratorListeners.removeAt(i); 384 } 385 } 386 } 387 onVibrating(int vibratorIdx, boolean vibrating)388 void onVibrating(int vibratorIdx, boolean vibrating) { 389 mExecutor.execute(() -> { 390 boolean anyVibrating; 391 synchronized (mLock) { 392 int allInitializedMask = 1 << mVibratorListeners.size() - 1; 393 int vibratorMask = 1 << vibratorIdx; 394 if ((mInitializedMask & vibratorMask) == 0) { 395 // First state report for this vibrator, set vibrating initial value. 396 mInitializedMask |= vibratorMask; 397 mVibratingMask |= vibrating ? vibratorMask : 0; 398 } else { 399 // Flip vibrating value, if changed. 400 boolean prevVibrating = (mVibratingMask & vibratorMask) != 0; 401 if (prevVibrating != vibrating) { 402 mVibratingMask ^= vibratorMask; 403 } 404 } 405 if (mInitializedMask != allInitializedMask) { 406 // Wait for all vibrators initial state to be reported before delegating. 407 return; 408 } 409 anyVibrating = mVibratingMask != 0; 410 } 411 mDelegate.onVibratorStateChanged(anyVibrating); 412 }); 413 } 414 } 415 } 416