1 /* 2 * Copyright (C) 2021 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 static android.os.Trace.TRACE_TAG_VIBRATOR; 20 21 import android.annotation.CallbackExecutor; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.Context; 25 import android.hardware.vibrator.IVibratorManager; 26 import android.os.vibrator.IVibrationSession; 27 import android.os.vibrator.IVibrationSessionCallback; 28 import android.os.vibrator.VendorVibrationSession; 29 import android.util.ArrayMap; 30 import android.util.Log; 31 import android.util.SparseArray; 32 33 import com.android.internal.annotations.GuardedBy; 34 35 import java.util.Objects; 36 import java.util.concurrent.Executor; 37 38 /** 39 * VibratorManager implementation that controls the system vibrators. 40 * 41 * @hide 42 */ 43 public class SystemVibratorManager extends VibratorManager { 44 private static final String TAG = "VibratorManager"; 45 46 private final IVibratorManagerService mService; 47 private final Context mContext; 48 private final int mUid; 49 private final Binder mToken = new Binder(); 50 private final Object mLock = new Object(); 51 @GuardedBy("mLock") 52 private int[] mVibratorIds; 53 @GuardedBy("mLock") 54 private int mCapabilities; 55 @GuardedBy("mLock") 56 private final SparseArray<Vibrator> mVibrators = new SparseArray<>(); 57 58 @GuardedBy("mLock") 59 private final ArrayMap<Vibrator.OnVibratorStateChangedListener, 60 OnVibratorStateChangedListenerDelegate> mListeners = new ArrayMap<>(); 61 62 /** 63 * @hide to prevent subclassing from outside of the framework 64 */ SystemVibratorManager(Context context)65 public SystemVibratorManager(Context context) { 66 super(context); 67 mContext = context; 68 mUid = Process.myUid(); 69 mService = IVibratorManagerService.Stub.asInterface( 70 ServiceManager.getService(Context.VIBRATOR_MANAGER_SERVICE)); 71 } 72 73 @NonNull 74 @Override getVibratorIds()75 public int[] getVibratorIds() { 76 synchronized (mLock) { 77 if (mVibratorIds != null) { 78 return mVibratorIds; 79 } 80 try { 81 if (mService == null) { 82 Log.w(TAG, "Failed to retrieve vibrator ids; no vibrator manager service."); 83 } else { 84 return mVibratorIds = mService.getVibratorIds(); 85 } 86 } catch (RemoteException e) { 87 e.rethrowFromSystemServer(); 88 } 89 return new int[0]; 90 } 91 } 92 93 @Override hasCapabilities(int capabilities)94 public boolean hasCapabilities(int capabilities) { 95 return (getCapabilities() & capabilities) == capabilities; 96 } 97 98 @NonNull 99 @Override getVibrator(int vibratorId)100 public Vibrator getVibrator(int vibratorId) { 101 synchronized (mLock) { 102 Vibrator vibrator = mVibrators.get(vibratorId); 103 if (vibrator != null) { 104 return vibrator; 105 } 106 VibratorInfo info = null; 107 try { 108 if (mService == null) { 109 Log.w(TAG, "Failed to retrieve vibrator; no vibrator manager service."); 110 } else { 111 info = mService.getVibratorInfo(vibratorId); 112 } 113 } catch (RemoteException e) { 114 e.rethrowFromSystemServer(); 115 } 116 if (info != null) { 117 vibrator = new SingleVibrator(info); 118 mVibrators.put(vibratorId, vibrator); 119 } else { 120 vibrator = NullVibrator.getInstance(); 121 } 122 return vibrator; 123 } 124 } 125 126 @NonNull 127 @Override getDefaultVibrator()128 public Vibrator getDefaultVibrator() { 129 return mContext.getSystemService(Vibrator.class); 130 } 131 132 @Override setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, @Nullable CombinedVibration effect, @Nullable VibrationAttributes attributes)133 public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, 134 @Nullable CombinedVibration effect, @Nullable VibrationAttributes attributes) { 135 if (mService == null) { 136 Log.w(TAG, "Failed to set always-on effect; no vibrator manager service."); 137 return false; 138 } 139 try { 140 return mService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, effect, attributes); 141 } catch (RemoteException e) { 142 Log.w(TAG, "Failed to set always-on effect.", e); 143 } 144 return false; 145 } 146 147 @Override vibrate(int uid, String opPkg, @NonNull CombinedVibration effect, String reason, @Nullable VibrationAttributes attributes)148 public void vibrate(int uid, String opPkg, @NonNull CombinedVibration effect, 149 String reason, @Nullable VibrationAttributes attributes) { 150 if (mService == null) { 151 Log.w(TAG, "Failed to vibrate; no vibrator manager service."); 152 return; 153 } 154 Trace.traceBegin(TRACE_TAG_VIBRATOR, "vibrate"); 155 try { 156 mService.vibrate(uid, mContext.getDeviceId(), opPkg, effect, attributes, reason, 157 mToken); 158 } catch (RemoteException e) { 159 Log.w(TAG, "Failed to vibrate.", e); 160 } finally { 161 Trace.traceEnd(TRACE_TAG_VIBRATOR); 162 } 163 } 164 165 @Override performHapticFeedback(int constant, String reason, int flags, int privFlags)166 public void performHapticFeedback(int constant, String reason, int flags, int privFlags) { 167 if (mService == null) { 168 Log.w(TAG, "Failed to perform haptic feedback; no vibrator manager service."); 169 return; 170 } 171 Trace.traceBegin(TRACE_TAG_VIBRATOR, "performHapticFeedback"); 172 try { 173 mService.performHapticFeedback(mUid, mContext.getDeviceId(), mPackageName, constant, 174 reason, flags, privFlags); 175 } catch (RemoteException e) { 176 Log.w(TAG, "Failed to perform haptic feedback.", e); 177 } finally { 178 Trace.traceEnd(TRACE_TAG_VIBRATOR); 179 } 180 } 181 182 @Override performHapticFeedbackForInputDevice(int constant, int inputDeviceId, int inputSource, String reason, int flags, int privFlags)183 public void performHapticFeedbackForInputDevice(int constant, int inputDeviceId, 184 int inputSource, String reason, int flags, int privFlags) { 185 if (mService == null) { 186 Log.w(TAG, "Failed to perform haptic feedback for input device;" 187 + " no vibrator manager service."); 188 return; 189 } 190 Trace.traceBegin(TRACE_TAG_VIBRATOR, "performHapticFeedbackForInputDevice"); 191 try { 192 mService.performHapticFeedbackForInputDevice(mUid, mContext.getDeviceId(), mPackageName, 193 constant, inputDeviceId, inputSource, reason, flags, privFlags); 194 } catch (RemoteException e) { 195 Log.w(TAG, "Failed to perform haptic feedback for input device.", e); 196 } finally { 197 Trace.traceEnd(TRACE_TAG_VIBRATOR); 198 } 199 } 200 201 @Override cancel()202 public void cancel() { 203 cancelVibration(VibrationAttributes.USAGE_FILTER_MATCH_ALL); 204 } 205 206 @Override cancel(int usageFilter)207 public void cancel(int usageFilter) { 208 cancelVibration(usageFilter); 209 } 210 211 @Override startVendorSession(@onNull int[] vibratorIds, @NonNull VibrationAttributes attrs, @Nullable String reason, @Nullable CancellationSignal cancellationSignal, @NonNull Executor executor, @NonNull VendorVibrationSession.Callback callback)212 public void startVendorSession(@NonNull int[] vibratorIds, @NonNull VibrationAttributes attrs, 213 @Nullable String reason, @Nullable CancellationSignal cancellationSignal, 214 @NonNull Executor executor, @NonNull VendorVibrationSession.Callback callback) { 215 Objects.requireNonNull(vibratorIds); 216 VendorVibrationSessionCallbackDelegate callbackDelegate = 217 new VendorVibrationSessionCallbackDelegate(executor, callback); 218 if (mService == null) { 219 Log.w(TAG, "Failed to start vibration session; no vibrator manager service."); 220 callbackDelegate.onFinished(VendorVibrationSession.STATUS_UNSUPPORTED); 221 return; 222 } 223 try { 224 ICancellationSignal remoteCancellationSignal = mService.startVendorVibrationSession( 225 mUid, mContext.getDeviceId(), mPackageName, vibratorIds, attrs, reason, 226 callbackDelegate); 227 if (cancellationSignal != null) { 228 cancellationSignal.setRemote(remoteCancellationSignal); 229 } 230 } catch (RemoteException e) { 231 Log.w(TAG, "Failed to start vibration session.", e); 232 callbackDelegate.onFinished(VendorVibrationSession.STATUS_UNKNOWN_ERROR); 233 } 234 } 235 getCapabilities()236 private int getCapabilities() { 237 synchronized (mLock) { 238 if (mCapabilities != 0) { 239 return mCapabilities; 240 } 241 try { 242 if (mService == null) { 243 Log.w(TAG, "Failed to retrieve vibrator manager capabilities;" 244 + " no vibrator manager service."); 245 } else { 246 return mCapabilities = mService.getCapabilities(); 247 } 248 } catch (RemoteException e) { 249 e.rethrowFromSystemServer(); 250 } 251 return 0; 252 } 253 } 254 cancelVibration(int usageFilter)255 private void cancelVibration(int usageFilter) { 256 if (mService == null) { 257 Log.w(TAG, "Failed to cancel vibration; no vibrator manager service."); 258 return; 259 } 260 try { 261 mService.cancelVibrate(usageFilter, mToken); 262 } catch (RemoteException e) { 263 Log.w(TAG, "Failed to cancel vibration.", e); 264 } 265 } 266 267 /** Listener for vibrations on a single vibrator. */ 268 private static class OnVibratorStateChangedListenerDelegate extends 269 IVibratorStateListener.Stub { 270 private final Executor mExecutor; 271 private final Vibrator.OnVibratorStateChangedListener mListener; 272 OnVibratorStateChangedListenerDelegate( @onNull Vibrator.OnVibratorStateChangedListener listener, @NonNull Executor executor)273 OnVibratorStateChangedListenerDelegate( 274 @NonNull Vibrator.OnVibratorStateChangedListener listener, 275 @NonNull Executor executor) { 276 mExecutor = executor; 277 mListener = listener; 278 } 279 280 @Override onVibrating(boolean isVibrating)281 public void onVibrating(boolean isVibrating) { 282 mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating)); 283 } 284 } 285 286 /** Callback for vendor vibration sessions. */ 287 private static class VendorVibrationSessionCallbackDelegate extends 288 IVibrationSessionCallback.Stub { 289 private final Executor mExecutor; 290 private final VendorVibrationSession.Callback mCallback; 291 VendorVibrationSessionCallbackDelegate( @onNull Executor executor, @NonNull VendorVibrationSession.Callback callback)292 VendorVibrationSessionCallbackDelegate( 293 @NonNull Executor executor, 294 @NonNull VendorVibrationSession.Callback callback) { 295 Objects.requireNonNull(executor); 296 Objects.requireNonNull(callback); 297 mExecutor = executor; 298 mCallback = callback; 299 } 300 301 @Override onStarted(IVibrationSession session)302 public void onStarted(IVibrationSession session) { 303 mExecutor.execute(() -> mCallback.onStarted(new VendorVibrationSession(session))); 304 } 305 306 @Override onFinishing()307 public void onFinishing() { 308 mExecutor.execute(() -> mCallback.onFinishing()); 309 } 310 311 @Override onFinished(int status)312 public void onFinished(int status) { 313 mExecutor.execute(() -> mCallback.onFinished(status)); 314 } 315 } 316 317 /** Controls vibrations on a single vibrator. */ 318 private final class SingleVibrator extends Vibrator { 319 private final VibratorInfo mVibratorInfo; 320 private final int[] mVibratorId; 321 SingleVibrator(@onNull VibratorInfo vibratorInfo)322 SingleVibrator(@NonNull VibratorInfo vibratorInfo) { 323 mVibratorInfo = vibratorInfo; 324 mVibratorId = new int[]{mVibratorInfo.getId()}; 325 } 326 327 @Override getInfo()328 public VibratorInfo getInfo() { 329 return mVibratorInfo; 330 } 331 332 @Override hasVibrator()333 public boolean hasVibrator() { 334 return true; 335 } 336 337 @Override hasAmplitudeControl()338 public boolean hasAmplitudeControl() { 339 return mVibratorInfo.hasAmplitudeControl(); 340 } 341 342 @Override areVendorSessionsSupported()343 public boolean areVendorSessionsSupported() { 344 return SystemVibratorManager.this.hasCapabilities(IVibratorManager.CAP_START_SESSIONS); 345 } 346 347 @Override setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, @Nullable VibrationEffect effect, @Nullable VibrationAttributes attrs)348 public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, 349 @Nullable VibrationEffect effect, @Nullable VibrationAttributes attrs) { 350 CombinedVibration combined = CombinedVibration.startParallel() 351 .addVibrator(mVibratorInfo.getId(), effect) 352 .combine(); 353 return SystemVibratorManager.this.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combined, 354 attrs); 355 } 356 357 @Override vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason, @NonNull VibrationAttributes attributes)358 public void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason, 359 @NonNull VibrationAttributes attributes) { 360 CombinedVibration combined = CombinedVibration.startParallel() 361 .addVibrator(mVibratorInfo.getId(), vibe) 362 .combine(); 363 SystemVibratorManager.this.vibrate(uid, opPkg, combined, reason, attributes); 364 } 365 366 @Override performHapticFeedback(int effectId, String reason, int flags, int privFlags)367 public void performHapticFeedback(int effectId, String reason, int flags, int privFlags) { 368 SystemVibratorManager.this.performHapticFeedback(effectId, reason, flags, privFlags); 369 } 370 371 @Override cancel()372 public void cancel() { 373 SystemVibratorManager.this.cancel(); 374 } 375 376 @Override cancel(int usageFilter)377 public void cancel(int usageFilter) { 378 SystemVibratorManager.this.cancel(usageFilter); 379 } 380 381 @Override isVibrating()382 public boolean isVibrating() { 383 if (mService == null) { 384 Log.w(TAG, "Failed to check status of vibrator " + mVibratorInfo.getId() 385 + "; no vibrator service."); 386 return false; 387 } 388 try { 389 return mService.isVibrating(mVibratorInfo.getId()); 390 } catch (RemoteException e) { 391 e.rethrowFromSystemServer(); 392 } 393 return false; 394 } 395 396 @Override addVibratorStateListener(@onNull OnVibratorStateChangedListener listener)397 public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { 398 Objects.requireNonNull(listener); 399 if (mContext == null) { 400 Log.w(TAG, "Failed to add vibrate state listener; no vibrator context."); 401 return; 402 } 403 addVibratorStateListener(mContext.getMainExecutor(), listener); 404 } 405 406 @Override addVibratorStateListener( @onNull @allbackExecutor Executor executor, @NonNull OnVibratorStateChangedListener listener)407 public void addVibratorStateListener( 408 @NonNull @CallbackExecutor Executor executor, 409 @NonNull OnVibratorStateChangedListener listener) { 410 Objects.requireNonNull(listener); 411 Objects.requireNonNull(executor); 412 if (mService == null) { 413 Log.w(TAG, 414 "Failed to add vibrate state listener to vibrator " + mVibratorInfo.getId() 415 + "; no vibrator service."); 416 return; 417 } 418 synchronized (mLock) { 419 // If listener is already registered, reject and return. 420 if (mListeners.containsKey(listener)) { 421 Log.w(TAG, "Listener already registered."); 422 return; 423 } 424 try { 425 OnVibratorStateChangedListenerDelegate delegate = 426 new OnVibratorStateChangedListenerDelegate(listener, executor); 427 if (!mService.registerVibratorStateListener(mVibratorInfo.getId(), delegate)) { 428 Log.w(TAG, "Failed to add vibrate state listener to vibrator " 429 + mVibratorInfo.getId()); 430 return; 431 } 432 mListeners.put(listener, delegate); 433 } catch (RemoteException e) { 434 e.rethrowFromSystemServer(); 435 } 436 } 437 } 438 439 @Override removeVibratorStateListener(@onNull OnVibratorStateChangedListener listener)440 public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { 441 Objects.requireNonNull(listener); 442 if (mService == null) { 443 Log.w(TAG, "Failed to remove vibrate state listener from vibrator " 444 + mVibratorInfo.getId() + "; no vibrator service."); 445 return; 446 } 447 synchronized (mLock) { 448 // Check if the listener is registered, otherwise will return. 449 if (mListeners.containsKey(listener)) { 450 OnVibratorStateChangedListenerDelegate delegate = mListeners.get(listener); 451 try { 452 if (!mService.unregisterVibratorStateListener(mVibratorInfo.getId(), 453 delegate)) { 454 Log.w(TAG, "Failed to remove vibrate state listener from vibrator " 455 + mVibratorInfo.getId()); 456 return; 457 } 458 mListeners.remove(listener); 459 } catch (RemoteException e) { 460 e.rethrowFromSystemServer(); 461 } 462 } 463 } 464 } 465 466 @Override startVendorSession(@onNull VibrationAttributes attrs, String reason, @Nullable CancellationSignal cancellationSignal, @NonNull Executor executor, @NonNull VendorVibrationSession.Callback callback)467 public void startVendorSession(@NonNull VibrationAttributes attrs, String reason, 468 @Nullable CancellationSignal cancellationSignal, @NonNull Executor executor, 469 @NonNull VendorVibrationSession.Callback callback) { 470 SystemVibratorManager.this.startVendorSession(mVibratorId, attrs, reason, 471 cancellationSignal, executor, callback); 472 } 473 } 474 } 475