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