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.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 
30 import java.util.Objects;
31 import java.util.concurrent.Executor;
32 
33 /**
34  * VibratorManager implementation that controls the system vibrators.
35  *
36  * @hide
37  */
38 public class SystemVibratorManager extends VibratorManager {
39     private static final String TAG = "VibratorManager";
40 
41     private final IVibratorManagerService mService;
42     private final Context mContext;
43     private final Binder mToken = new Binder();
44     private final Object mLock = new Object();
45     @GuardedBy("mLock")
46     private int[] mVibratorIds;
47     @GuardedBy("mLock")
48     private final SparseArray<Vibrator> mVibrators = new SparseArray<>();
49 
50     @GuardedBy("mLock")
51     private final ArrayMap<Vibrator.OnVibratorStateChangedListener,
52             OnVibratorStateChangedListenerDelegate> mListeners = new ArrayMap<>();
53 
54     /**
55      * @hide to prevent subclassing from outside of the framework
56      */
SystemVibratorManager(Context context)57     public SystemVibratorManager(Context context) {
58         super(context);
59         mContext = context;
60         mService = IVibratorManagerService.Stub.asInterface(
61                 ServiceManager.getService(Context.VIBRATOR_MANAGER_SERVICE));
62     }
63 
64     @NonNull
65     @Override
getVibratorIds()66     public int[] getVibratorIds() {
67         synchronized (mLock) {
68             if (mVibratorIds != null) {
69                 return mVibratorIds;
70             }
71             try {
72                 if (mService == null) {
73                     Log.w(TAG, "Failed to retrieve vibrator ids; no vibrator manager service.");
74                 } else {
75                     return mVibratorIds = mService.getVibratorIds();
76                 }
77             } catch (RemoteException e) {
78                 e.rethrowFromSystemServer();
79             }
80             return new int[0];
81         }
82     }
83 
84     @NonNull
85     @Override
getVibrator(int vibratorId)86     public Vibrator getVibrator(int vibratorId) {
87         synchronized (mLock) {
88             Vibrator vibrator = mVibrators.get(vibratorId);
89             if (vibrator != null) {
90                 return vibrator;
91             }
92             VibratorInfo info = null;
93             try {
94                 if (mService == null) {
95                     Log.w(TAG, "Failed to retrieve vibrator; no vibrator manager service.");
96                 } else {
97                     info = mService.getVibratorInfo(vibratorId);
98                 }
99             } catch (RemoteException e) {
100                 e.rethrowFromSystemServer();
101             }
102             if (info != null) {
103                 vibrator = new SingleVibrator(info);
104                 mVibrators.put(vibratorId, vibrator);
105             } else {
106                 vibrator = NullVibrator.getInstance();
107             }
108             return vibrator;
109         }
110     }
111 
112     @NonNull
113     @Override
getDefaultVibrator()114     public Vibrator getDefaultVibrator() {
115         return mContext.getSystemService(Vibrator.class);
116     }
117 
118     @Override
setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, @Nullable CombinedVibration effect, @Nullable VibrationAttributes attributes)119     public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
120             @Nullable CombinedVibration effect, @Nullable VibrationAttributes attributes) {
121         if (mService == null) {
122             Log.w(TAG, "Failed to set always-on effect; no vibrator manager service.");
123             return false;
124         }
125         try {
126             return mService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, effect, attributes);
127         } catch (RemoteException e) {
128             Log.w(TAG, "Failed to set always-on effect.", e);
129         }
130         return false;
131     }
132 
133     @Override
vibrate(int uid, String opPkg, @NonNull CombinedVibration effect, String reason, @Nullable VibrationAttributes attributes)134     public void vibrate(int uid, String opPkg, @NonNull CombinedVibration effect,
135             String reason, @Nullable VibrationAttributes attributes) {
136         if (mService == null) {
137             Log.w(TAG, "Failed to vibrate; no vibrator manager service.");
138             return;
139         }
140         try {
141             mService.vibrate(uid, opPkg, effect, attributes, 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 AudioAttributes attributes)212         public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
213                 @Nullable VibrationEffect effect, @Nullable AudioAttributes attributes) {
214             VibrationAttributes attr = new VibrationAttributes.Builder(
215                     attributes, effect).build();
216             CombinedVibration combined = CombinedVibration.startParallel()
217                     .addVibrator(mVibratorInfo.getId(), effect)
218                     .combine();
219             return SystemVibratorManager.this.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combined,
220                     attr);
221         }
222 
223         @Override
vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason, @NonNull VibrationAttributes attributes)224         public void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason,
225                 @NonNull VibrationAttributes attributes) {
226             CombinedVibration combined = CombinedVibration.startParallel()
227                     .addVibrator(mVibratorInfo.getId(), vibe)
228                     .combine();
229             SystemVibratorManager.this.vibrate(uid, opPkg, combined, reason, attributes);
230         }
231 
232         @Override
cancel()233         public void cancel() {
234             SystemVibratorManager.this.cancel();
235         }
236 
237         @Override
cancel(int usageFilter)238         public void cancel(int usageFilter) {
239             SystemVibratorManager.this.cancel(usageFilter);
240         }
241 
242         @Override
isVibrating()243         public boolean isVibrating() {
244             if (mService == null) {
245                 Log.w(TAG, "Failed to check status of vibrator " + mVibratorInfo.getId()
246                         + "; no vibrator service.");
247                 return false;
248             }
249             try {
250                 return mService.isVibrating(mVibratorInfo.getId());
251             } catch (RemoteException e) {
252                 e.rethrowFromSystemServer();
253             }
254             return false;
255         }
256 
257         @Override
addVibratorStateListener(@onNull OnVibratorStateChangedListener listener)258         public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
259             Objects.requireNonNull(listener);
260             if (mContext == null) {
261                 Log.w(TAG, "Failed to add vibrate state listener; no vibrator context.");
262                 return;
263             }
264             addVibratorStateListener(mContext.getMainExecutor(), listener);
265         }
266 
267         @Override
addVibratorStateListener( @onNull @allbackExecutor Executor executor, @NonNull OnVibratorStateChangedListener listener)268         public void addVibratorStateListener(
269                 @NonNull @CallbackExecutor Executor executor,
270                 @NonNull OnVibratorStateChangedListener listener) {
271             Objects.requireNonNull(listener);
272             Objects.requireNonNull(executor);
273             if (mService == null) {
274                 Log.w(TAG,
275                         "Failed to add vibrate state listener to vibrator " + mVibratorInfo.getId()
276                                 + "; no vibrator service.");
277                 return;
278             }
279             synchronized (mLock) {
280                 // If listener is already registered, reject and return.
281                 if (mListeners.containsKey(listener)) {
282                     Log.w(TAG, "Listener already registered.");
283                     return;
284                 }
285                 try {
286                     OnVibratorStateChangedListenerDelegate delegate =
287                             new OnVibratorStateChangedListenerDelegate(listener, executor);
288                     if (!mService.registerVibratorStateListener(mVibratorInfo.getId(), delegate)) {
289                         Log.w(TAG, "Failed to add vibrate state listener to vibrator "
290                                 + mVibratorInfo.getId());
291                         return;
292                     }
293                     mListeners.put(listener, delegate);
294                 } catch (RemoteException e) {
295                     e.rethrowFromSystemServer();
296                 }
297             }
298         }
299 
300         @Override
removeVibratorStateListener(@onNull OnVibratorStateChangedListener listener)301         public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
302             Objects.requireNonNull(listener);
303             if (mService == null) {
304                 Log.w(TAG, "Failed to remove vibrate state listener from vibrator "
305                         + mVibratorInfo.getId() + "; no vibrator service.");
306                 return;
307             }
308             synchronized (mLock) {
309                 // Check if the listener is registered, otherwise will return.
310                 if (mListeners.containsKey(listener)) {
311                     OnVibratorStateChangedListenerDelegate delegate = mListeners.get(listener);
312                     try {
313                         if (!mService.unregisterVibratorStateListener(mVibratorInfo.getId(),
314                                 delegate)) {
315                             Log.w(TAG, "Failed to remove vibrate state listener from vibrator "
316                                     + mVibratorInfo.getId());
317                             return;
318                         }
319                         mListeners.remove(listener);
320                     } catch (RemoteException e) {
321                         e.rethrowFromSystemServer();
322                     }
323                 }
324             }
325         }
326     }
327 }
328