• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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