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