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