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