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 android.annotation.CallbackExecutor;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.content.Context;
23 import android.util.ArrayMap;
24 import android.util.Log;
25 import android.util.SparseArray;
26 
27 import com.android.internal.annotations.GuardedBy;
28 
29 import java.util.Objects;
30 import java.util.concurrent.Executor;
31 
32 /**
33  * VibratorManager implementation that controls the system vibrators.
34  *
35  * @hide
36  */
37 public class SystemVibratorManager extends VibratorManager {
38     private static final String TAG = "VibratorManager";
39 
40     private final IVibratorManagerService mService;
41     private final Context mContext;
42     private final Binder mToken = new Binder();
43     private final Object mLock = new Object();
44     @GuardedBy("mLock")
45     private int[] mVibratorIds;
46     @GuardedBy("mLock")
47     private final SparseArray<Vibrator> mVibrators = new SparseArray<>();
48 
49     @GuardedBy("mLock")
50     private final ArrayMap<Vibrator.OnVibratorStateChangedListener,
51             OnVibratorStateChangedListenerDelegate> mListeners = new ArrayMap<>();
52 
53     /**
54      * @hide to prevent subclassing from outside of the framework
55      */
SystemVibratorManager(Context context)56     public SystemVibratorManager(Context context) {
57         super(context);
58         mContext = context;
59         mService = IVibratorManagerService.Stub.asInterface(
60                 ServiceManager.getService(Context.VIBRATOR_MANAGER_SERVICE));
61     }
62 
63     @NonNull
64     @Override
getVibratorIds()65     public int[] getVibratorIds() {
66         synchronized (mLock) {
67             if (mVibratorIds != null) {
68                 return mVibratorIds;
69             }
70             try {
71                 if (mService == null) {
72                     Log.w(TAG, "Failed to retrieve vibrator ids; no vibrator manager service.");
73                 } else {
74                     return mVibratorIds = mService.getVibratorIds();
75                 }
76             } catch (RemoteException e) {
77                 e.rethrowFromSystemServer();
78             }
79             return new int[0];
80         }
81     }
82 
83     @NonNull
84     @Override
getVibrator(int vibratorId)85     public Vibrator getVibrator(int vibratorId) {
86         synchronized (mLock) {
87             Vibrator vibrator = mVibrators.get(vibratorId);
88             if (vibrator != null) {
89                 return vibrator;
90             }
91             VibratorInfo info = null;
92             try {
93                 if (mService == null) {
94                     Log.w(TAG, "Failed to retrieve vibrator; no vibrator manager service.");
95                 } else {
96                     info = mService.getVibratorInfo(vibratorId);
97                 }
98             } catch (RemoteException e) {
99                 e.rethrowFromSystemServer();
100             }
101             if (info != null) {
102                 vibrator = new SingleVibrator(info);
103                 mVibrators.put(vibratorId, vibrator);
104             } else {
105                 vibrator = NullVibrator.getInstance();
106             }
107             return vibrator;
108         }
109     }
110 
111     @NonNull
112     @Override
getDefaultVibrator()113     public Vibrator getDefaultVibrator() {
114         return mContext.getSystemService(Vibrator.class);
115     }
116 
117     @Override
setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, @Nullable CombinedVibration effect, @Nullable VibrationAttributes attributes)118     public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
119             @Nullable CombinedVibration effect, @Nullable VibrationAttributes attributes) {
120         if (mService == null) {
121             Log.w(TAG, "Failed to set always-on effect; no vibrator manager service.");
122             return false;
123         }
124         try {
125             return mService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, effect, attributes);
126         } catch (RemoteException e) {
127             Log.w(TAG, "Failed to set always-on effect.", e);
128         }
129         return false;
130     }
131 
132     @Override
vibrate(int uid, String opPkg, @NonNull CombinedVibration effect, String reason, @Nullable VibrationAttributes attributes)133     public void vibrate(int uid, String opPkg, @NonNull CombinedVibration effect,
134             String reason, @Nullable VibrationAttributes attributes) {
135         if (mService == null) {
136             Log.w(TAG, "Failed to vibrate; no vibrator manager service.");
137             return;
138         }
139         try {
140             mService.vibrate(uid, mContext.getAssociatedDisplayId(), opPkg, effect, attributes,
141                     reason, mToken);
142         } catch (RemoteException e) {
143             Log.w(TAG, "Failed to vibrate.", e);
144         }
145     }
146 
147     @Override
cancel()148     public void cancel() {
149         cancelVibration(VibrationAttributes.USAGE_FILTER_MATCH_ALL);
150     }
151 
152     @Override
cancel(int usageFilter)153     public void cancel(int usageFilter) {
154         cancelVibration(usageFilter);
155     }
156 
cancelVibration(int usageFilter)157     private void cancelVibration(int usageFilter) {
158         if (mService == null) {
159             Log.w(TAG, "Failed to cancel vibration; no vibrator manager service.");
160             return;
161         }
162         try {
163             mService.cancelVibrate(usageFilter, mToken);
164         } catch (RemoteException e) {
165             Log.w(TAG, "Failed to cancel vibration.", e);
166         }
167     }
168 
169     /** Listener for vibrations on a single vibrator. */
170     private static class OnVibratorStateChangedListenerDelegate extends
171             IVibratorStateListener.Stub {
172         private final Executor mExecutor;
173         private final Vibrator.OnVibratorStateChangedListener mListener;
174 
OnVibratorStateChangedListenerDelegate( @onNull Vibrator.OnVibratorStateChangedListener listener, @NonNull Executor executor)175         OnVibratorStateChangedListenerDelegate(
176                 @NonNull Vibrator.OnVibratorStateChangedListener listener,
177                 @NonNull Executor executor) {
178             mExecutor = executor;
179             mListener = listener;
180         }
181 
182         @Override
onVibrating(boolean isVibrating)183         public void onVibrating(boolean isVibrating) {
184             mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating));
185         }
186     }
187 
188     /** Controls vibrations on a single vibrator. */
189     private final class SingleVibrator extends Vibrator {
190         private final VibratorInfo mVibratorInfo;
191 
SingleVibrator(@onNull VibratorInfo vibratorInfo)192         SingleVibrator(@NonNull VibratorInfo vibratorInfo) {
193             mVibratorInfo = vibratorInfo;
194         }
195 
196         @Override
getInfo()197         protected VibratorInfo getInfo() {
198             return mVibratorInfo;
199         }
200 
201         @Override
hasVibrator()202         public boolean hasVibrator() {
203             return true;
204         }
205 
206         @Override
hasAmplitudeControl()207         public boolean hasAmplitudeControl() {
208             return mVibratorInfo.hasAmplitudeControl();
209         }
210 
211         @Override
setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, @Nullable VibrationEffect effect, @Nullable VibrationAttributes attrs)212         public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
213                 @Nullable VibrationEffect effect, @Nullable VibrationAttributes attrs) {
214             CombinedVibration combined = CombinedVibration.startParallel()
215                     .addVibrator(mVibratorInfo.getId(), effect)
216                     .combine();
217             return SystemVibratorManager.this.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combined,
218                     attrs);
219         }
220 
221         @Override
vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason, @NonNull VibrationAttributes attributes)222         public void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason,
223                 @NonNull VibrationAttributes attributes) {
224             CombinedVibration combined = CombinedVibration.startParallel()
225                     .addVibrator(mVibratorInfo.getId(), vibe)
226                     .combine();
227             SystemVibratorManager.this.vibrate(uid, opPkg, combined, reason, attributes);
228         }
229 
230         @Override
cancel()231         public void cancel() {
232             SystemVibratorManager.this.cancel();
233         }
234 
235         @Override
cancel(int usageFilter)236         public void cancel(int usageFilter) {
237             SystemVibratorManager.this.cancel(usageFilter);
238         }
239 
240         @Override
isVibrating()241         public boolean isVibrating() {
242             if (mService == null) {
243                 Log.w(TAG, "Failed to check status of vibrator " + mVibratorInfo.getId()
244                         + "; no vibrator service.");
245                 return false;
246             }
247             try {
248                 return mService.isVibrating(mVibratorInfo.getId());
249             } catch (RemoteException e) {
250                 e.rethrowFromSystemServer();
251             }
252             return false;
253         }
254 
255         @Override
addVibratorStateListener(@onNull OnVibratorStateChangedListener listener)256         public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
257             Objects.requireNonNull(listener);
258             if (mContext == null) {
259                 Log.w(TAG, "Failed to add vibrate state listener; no vibrator context.");
260                 return;
261             }
262             addVibratorStateListener(mContext.getMainExecutor(), listener);
263         }
264 
265         @Override
addVibratorStateListener( @onNull @allbackExecutor Executor executor, @NonNull OnVibratorStateChangedListener listener)266         public void addVibratorStateListener(
267                 @NonNull @CallbackExecutor Executor executor,
268                 @NonNull OnVibratorStateChangedListener listener) {
269             Objects.requireNonNull(listener);
270             Objects.requireNonNull(executor);
271             if (mService == null) {
272                 Log.w(TAG,
273                         "Failed to add vibrate state listener to vibrator " + mVibratorInfo.getId()
274                                 + "; no vibrator service.");
275                 return;
276             }
277             synchronized (mLock) {
278                 // If listener is already registered, reject and return.
279                 if (mListeners.containsKey(listener)) {
280                     Log.w(TAG, "Listener already registered.");
281                     return;
282                 }
283                 try {
284                     OnVibratorStateChangedListenerDelegate delegate =
285                             new OnVibratorStateChangedListenerDelegate(listener, executor);
286                     if (!mService.registerVibratorStateListener(mVibratorInfo.getId(), delegate)) {
287                         Log.w(TAG, "Failed to add vibrate state listener to vibrator "
288                                 + mVibratorInfo.getId());
289                         return;
290                     }
291                     mListeners.put(listener, delegate);
292                 } catch (RemoteException e) {
293                     e.rethrowFromSystemServer();
294                 }
295             }
296         }
297 
298         @Override
removeVibratorStateListener(@onNull OnVibratorStateChangedListener listener)299         public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
300             Objects.requireNonNull(listener);
301             if (mService == null) {
302                 Log.w(TAG, "Failed to remove vibrate state listener from vibrator "
303                         + mVibratorInfo.getId() + "; no vibrator service.");
304                 return;
305             }
306             synchronized (mLock) {
307                 // Check if the listener is registered, otherwise will return.
308                 if (mListeners.containsKey(listener)) {
309                     OnVibratorStateChangedListenerDelegate delegate = mListeners.get(listener);
310                     try {
311                         if (!mService.unregisterVibratorStateListener(mVibratorInfo.getId(),
312                                 delegate)) {
313                             Log.w(TAG, "Failed to remove vibrate state listener from vibrator "
314                                     + mVibratorInfo.getId());
315                             return;
316                         }
317                         mListeners.remove(listener);
318                     } catch (RemoteException e) {
319                         e.rethrowFromSystemServer();
320                     }
321                 }
322             }
323         }
324     }
325 }
326