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 com.android.server.vibrator;
18 
19 import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
20 import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.app.ActivityManager;
25 import android.app.AppOpsManager;
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.content.pm.PackageManager;
31 import android.content.res.Resources;
32 import android.hardware.vibrator.IVibrator;
33 import android.os.BatteryStats;
34 import android.os.Binder;
35 import android.os.Build;
36 import android.os.CombinedVibration;
37 import android.os.ExternalVibration;
38 import android.os.ExternalVibrationScale;
39 import android.os.Handler;
40 import android.os.IBinder;
41 import android.os.IExternalVibratorService;
42 import android.os.IVibratorManagerService;
43 import android.os.IVibratorStateListener;
44 import android.os.Looper;
45 import android.os.PowerManager;
46 import android.os.Process;
47 import android.os.RemoteException;
48 import android.os.ResultReceiver;
49 import android.os.ServiceManager;
50 import android.os.ShellCallback;
51 import android.os.ShellCommand;
52 import android.os.SystemClock;
53 import android.os.Trace;
54 import android.os.VibrationAttributes;
55 import android.os.VibrationEffect;
56 import android.os.VibratorInfo;
57 import android.os.vibrator.Flags;
58 import android.os.vibrator.PrebakedSegment;
59 import android.os.vibrator.VibrationEffectSegment;
60 import android.os.vibrator.VibratorInfoFactory;
61 import android.os.vibrator.persistence.ParsedVibration;
62 import android.os.vibrator.persistence.VibrationXmlParser;
63 import android.text.TextUtils;
64 import android.util.IndentingPrintWriter;
65 import android.util.Slog;
66 import android.util.SparseArray;
67 import android.util.proto.ProtoOutputStream;
68 
69 import com.android.internal.annotations.GuardedBy;
70 import com.android.internal.annotations.VisibleForTesting;
71 import com.android.internal.app.IBatteryStats;
72 import com.android.internal.util.DumpUtils;
73 import com.android.internal.util.FrameworkStatsLog;
74 import com.android.server.SystemService;
75 
76 import libcore.util.NativeAllocationRegistry;
77 
78 import java.io.FileDescriptor;
79 import java.io.IOException;
80 import java.io.PrintWriter;
81 import java.io.StringReader;
82 import java.lang.ref.WeakReference;
83 import java.time.Duration;
84 import java.util.ArrayList;
85 import java.util.Arrays;
86 import java.util.List;
87 import java.util.Objects;
88 import java.util.concurrent.CompletableFuture;
89 import java.util.function.Consumer;
90 import java.util.function.Function;
91 
92 /** System implementation of {@link IVibratorManagerService}. */
93 public class VibratorManagerService extends IVibratorManagerService.Stub {
94     private static final String TAG = "VibratorManagerService";
95     private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
96     private static final String VIBRATOR_CONTROL_SERVICE =
97             "android.frameworks.vibrator.IVibratorControlService/default";
98     private static final boolean DEBUG = false;
99     private static final VibrationAttributes DEFAULT_ATTRIBUTES =
100             new VibrationAttributes.Builder().build();
101     private static final int ATTRIBUTES_ALL_BYPASS_FLAGS =
102             VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
103                     | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
104                     | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
105 
106     /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
107     private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
108 
109     /**
110      * Maximum millis to wait for a vibration thread cancellation to "clean up" and finish, when
111      * blocking for an external vibration. In practice, this should be plenty.
112      */
113     private static final long VIBRATION_CANCEL_WAIT_MILLIS = 5000;
114 
115     /** Lifecycle responsible for initializing this class at the right system server phases. */
116     public static class Lifecycle extends SystemService {
117         private VibratorManagerService mService;
118 
Lifecycle(Context context)119         public Lifecycle(Context context) {
120             super(context);
121         }
122 
123         @Override
onStart()124         public void onStart() {
125             mService = new VibratorManagerService(getContext(), new Injector());
126             publishBinderService(Context.VIBRATOR_MANAGER_SERVICE, mService);
127         }
128 
129         @Override
onBootPhase(int phase)130         public void onBootPhase(int phase) {
131             if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
132                 mService.systemReady();
133             }
134         }
135     }
136 
137     private final Object mLock = new Object();
138     private final Context mContext;
139     private final Injector mInjector;
140     private final PowerManager.WakeLock mWakeLock;
141     private final IBatteryStats mBatteryStatsService;
142     private final VibratorFrameworkStatsLogger mFrameworkStatsLogger;
143     private final Handler mHandler;
144     private final VibrationThread mVibrationThread;
145     private final AppOpsManager mAppOps;
146     private final NativeWrapper mNativeWrapper;
147     private final VibratorManagerRecords mVibratorManagerRecords;
148     private final long mCapabilities;
149     private final int[] mVibratorIds;
150     private final SparseArray<VibratorController> mVibrators;
151     private final VibrationThreadCallbacks mVibrationThreadCallbacks =
152             new VibrationThreadCallbacks();
153     @GuardedBy("mLock")
154     private final SparseArray<AlwaysOnVibration> mAlwaysOnEffects = new SparseArray<>();
155     @GuardedBy("mLock")
156     private VibrationStepConductor mCurrentVibration;
157     @GuardedBy("mLock")
158     private VibrationStepConductor mNextVibration;
159     @GuardedBy("mLock")
160     private ExternalVibrationHolder mCurrentExternalVibration;
161     @GuardedBy("mLock")
162     private boolean mServiceReady;
163 
164     private final VibrationSettings mVibrationSettings;
165     private final VibrationScaler mVibrationScaler;
166     private final VibratorControlService mVibratorControlService;
167     private final InputDeviceDelegate mInputDeviceDelegate;
168     private final DeviceAdapter mDeviceAdapter;
169 
170     @GuardedBy("mLock")
171     @Nullable private VibratorInfo mCombinedVibratorInfo;
172     @GuardedBy("mLock")
173     @Nullable private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider;
174 
175     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
176         @Override
177         public void onReceive(Context context, Intent intent) {
178             if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
179                 synchronized (mLock) {
180                     // When the system is entering a non-interactive state, we want to cancel
181                     // vibrations in case a misbehaving app has abandoned them.
182                     if (shouldCancelOnScreenOffLocked(mNextVibration)) {
183                         clearNextVibrationLocked(
184                                 new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF));
185                     }
186                     if (shouldCancelOnScreenOffLocked(mCurrentVibration)) {
187                         mCurrentVibration.notifyCancelled(
188                                 new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
189                                 /* immediate= */ false);
190                     }
191                 }
192             }
193         }
194     };
195 
nativeInit(OnSyncedVibrationCompleteListener listener)196     static native long nativeInit(OnSyncedVibrationCompleteListener listener);
197 
nativeGetFinalizer()198     static native long nativeGetFinalizer();
199 
nativeGetCapabilities(long nativeServicePtr)200     static native long nativeGetCapabilities(long nativeServicePtr);
201 
nativeGetVibratorIds(long nativeServicePtr)202     static native int[] nativeGetVibratorIds(long nativeServicePtr);
203 
nativePrepareSynced(long nativeServicePtr, int[] vibratorIds)204     static native boolean nativePrepareSynced(long nativeServicePtr, int[] vibratorIds);
205 
nativeTriggerSynced(long nativeServicePtr, long vibrationId)206     static native boolean nativeTriggerSynced(long nativeServicePtr, long vibrationId);
207 
nativeCancelSynced(long nativeServicePtr)208     static native void nativeCancelSynced(long nativeServicePtr);
209 
210     @VisibleForTesting
VibratorManagerService(Context context, Injector injector)211     VibratorManagerService(Context context, Injector injector) {
212         mContext = context;
213         mInjector = injector;
214         mHandler = injector.createHandler(Looper.myLooper());
215         mFrameworkStatsLogger = injector.getFrameworkStatsLogger(mHandler);
216 
217         mVibrationSettings = new VibrationSettings(mContext, mHandler);
218         mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings);
219         mVibratorControlService = new VibratorControlService(mContext,
220                 injector.createVibratorControllerHolder(), mVibrationScaler, mVibrationSettings,
221                 mFrameworkStatsLogger, mLock);
222         mInputDeviceDelegate = new InputDeviceDelegate(mContext, mHandler);
223 
224         VibrationCompleteListener listener = new VibrationCompleteListener(this);
225         mNativeWrapper = injector.getNativeWrapper();
226         mNativeWrapper.init(listener);
227 
228         int recentDumpSizeLimit = mContext.getResources().getInteger(
229                 com.android.internal.R.integer.config_recentVibrationsDumpSizeLimit);
230         int dumpSizeLimit = mContext.getResources().getInteger(
231                 com.android.internal.R.integer.config_previousVibrationsDumpSizeLimit);
232         int dumpAggregationTimeLimit = mContext.getResources().getInteger(
233                 com.android.internal.R.integer
234                         .config_previousVibrationsDumpAggregationTimeMillisLimit);
235         mVibratorManagerRecords = new VibratorManagerRecords(
236                 recentDumpSizeLimit, dumpSizeLimit, dumpAggregationTimeLimit);
237 
238         mBatteryStatsService = injector.getBatteryStatsService();
239 
240         mAppOps = mContext.getSystemService(AppOpsManager.class);
241 
242         PowerManager pm = context.getSystemService(PowerManager.class);
243         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
244         mWakeLock.setReferenceCounted(true);
245         mVibrationThread = new VibrationThread(mWakeLock, mVibrationThreadCallbacks);
246         mVibrationThread.start();
247 
248         // Load vibrator hardware info. The vibrator ids and manager capabilities are loaded only
249         // once and assumed unchanged for the lifecycle of this service. Each individual vibrator
250         // can still retry loading each individual vibrator hardware spec once more at systemReady.
251         mCapabilities = mNativeWrapper.getCapabilities();
252         int[] vibratorIds = mNativeWrapper.getVibratorIds();
253         if (vibratorIds == null) {
254             mVibratorIds = new int[0];
255             mVibrators = new SparseArray<>(0);
256         } else {
257             // Keep original vibrator id order, which might be meaningful.
258             mVibratorIds = vibratorIds;
259             mVibrators = new SparseArray<>(mVibratorIds.length);
260             for (int vibratorId : vibratorIds) {
261                 mVibrators.put(vibratorId, injector.createVibratorController(vibratorId, listener));
262             }
263         }
264 
265         // Load vibrator adapter, that depends on hardware info.
266         mDeviceAdapter = new DeviceAdapter(mVibrationSettings, mVibrators);
267 
268         // Reset the hardware to a default state, in case this is a runtime restart instead of a
269         // fresh boot.
270         mNativeWrapper.cancelSynced();
271         for (int i = 0; i < mVibrators.size(); i++) {
272             mVibrators.valueAt(i).reset();
273         }
274 
275         IntentFilter filter = new IntentFilter();
276         filter.addAction(Intent.ACTION_SCREEN_OFF);
277         context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
278 
279         injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
280         if (injector.isServiceDeclared(VIBRATOR_CONTROL_SERVICE)) {
281             injector.addService(VIBRATOR_CONTROL_SERVICE, mVibratorControlService);
282         }
283 
284     }
285 
286     /** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */
287     @VisibleForTesting
systemReady()288     void systemReady() {
289         Slog.v(TAG, "Initializing VibratorManager service...");
290         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "systemReady");
291         try {
292             // Will retry to load each vibrator's info, if any request have failed.
293             for (int i = 0; i < mVibrators.size(); i++) {
294                 mVibrators.valueAt(i).reloadVibratorInfoIfNeeded();
295             }
296 
297             mVibrationSettings.onSystemReady();
298             mInputDeviceDelegate.onSystemReady();
299 
300             mVibrationSettings.addListener(this::updateServiceState);
301 
302             // Will update settings and input devices.
303             updateServiceState();
304         } finally {
305             synchronized (mLock) {
306                 mServiceReady = true;
307             }
308             Slog.v(TAG, "VibratorManager service initialized");
309             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
310         }
311     }
312 
313     @Override // Binder call
getVibratorIds()314     public int[] getVibratorIds() {
315         return Arrays.copyOf(mVibratorIds, mVibratorIds.length);
316     }
317 
318     @Override // Binder call
319     @Nullable
getVibratorInfo(int vibratorId)320     public VibratorInfo getVibratorInfo(int vibratorId) {
321         final VibratorController controller = mVibrators.get(vibratorId);
322         if (controller == null) {
323             return null;
324         }
325         final VibratorInfo info = controller.getVibratorInfo();
326         synchronized (mLock) {
327             if (mServiceReady) {
328                 return info;
329             }
330         }
331         // If the service is not ready and the load was unsuccessful then return null while waiting
332         // for the service to be ready. It will retry to load the complete info from the HAL.
333         return controller.isVibratorInfoLoadSuccessful() ? info : null;
334     }
335 
336     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
337     @Override // Binder call
isVibrating(int vibratorId)338     public boolean isVibrating(int vibratorId) {
339         isVibrating_enforcePermission();
340         VibratorController controller = mVibrators.get(vibratorId);
341         return controller != null && controller.isVibrating();
342     }
343 
344     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
345     @Override // Binder call
registerVibratorStateListener(int vibratorId, IVibratorStateListener listener)346     public boolean registerVibratorStateListener(int vibratorId, IVibratorStateListener listener) {
347         registerVibratorStateListener_enforcePermission();
348         VibratorController controller = mVibrators.get(vibratorId);
349         if (controller == null) {
350             return false;
351         }
352         return controller.registerVibratorStateListener(listener);
353     }
354 
355     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
356     @Override // Binder call
unregisterVibratorStateListener(int vibratorId, IVibratorStateListener listener)357     public boolean unregisterVibratorStateListener(int vibratorId,
358             IVibratorStateListener listener) {
359         unregisterVibratorStateListener_enforcePermission();
360         VibratorController controller = mVibrators.get(vibratorId);
361         if (controller == null) {
362             return false;
363         }
364         return controller.unregisterVibratorStateListener(listener);
365     }
366 
367     @Override // Binder call
setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, @Nullable CombinedVibration effect, @Nullable VibrationAttributes attrs)368     public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
369             @Nullable CombinedVibration effect, @Nullable VibrationAttributes attrs) {
370         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "setAlwaysOnEffect");
371         try {
372             mContext.enforceCallingOrSelfPermission(
373                     android.Manifest.permission.VIBRATE_ALWAYS_ON,
374                     "setAlwaysOnEffect");
375 
376             if (effect == null) {
377                 synchronized (mLock) {
378                     mAlwaysOnEffects.delete(alwaysOnId);
379                     onAllVibratorsLocked(v -> {
380                         if (v.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
381                             v.updateAlwaysOn(alwaysOnId, /* effect= */ null);
382                         }
383                     });
384                 }
385                 return true;
386             }
387             if (!isEffectValid(effect)) {
388                 return false;
389             }
390             attrs = fixupVibrationAttributes(attrs, effect);
391             synchronized (mLock) {
392                 SparseArray<PrebakedSegment> effects = fixupAlwaysOnEffectsLocked(effect);
393                 if (effects == null) {
394                     // Invalid effects set in CombinedVibrationEffect, or always-on capability is
395                     // missing on individual vibrators.
396                     return false;
397                 }
398                 AlwaysOnVibration alwaysOnVibration = new AlwaysOnVibration(alwaysOnId,
399                         new Vibration.CallerInfo(attrs, uid, Context.DEVICE_ID_DEFAULT, opPkg,
400                                 null), effects);
401                 mAlwaysOnEffects.put(alwaysOnId, alwaysOnVibration);
402                 updateAlwaysOnLocked(alwaysOnVibration);
403             }
404             return true;
405         } finally {
406             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
407         }
408     }
409 
410     @Override // Binder call
vibrate(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs, String reason, IBinder token)411     public void vibrate(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect,
412             @Nullable VibrationAttributes attrs, String reason, IBinder token) {
413         vibrateWithPermissionCheck(uid, deviceId, opPkg, effect, attrs, reason, token);
414     }
415 
416     @Override // Binder call
performHapticFeedback(int uid, int deviceId, String opPkg, int constant, boolean always, String reason, boolean fromIme)417     public void performHapticFeedback(int uid, int deviceId, String opPkg, int constant,
418             boolean always, String reason, boolean fromIme) {
419         // Note that the `performHapticFeedback` method does not take a token argument from the
420         // caller, and instead, uses this service as the token. This is to mitigate performance
421         // impact that would otherwise be caused due to marshal latency. Haptic feedback effects are
422         // short-lived, so we don't need to cancel when the process dies.
423         performHapticFeedbackInternal(
424                 uid, deviceId, opPkg, constant, always, reason, /* token= */ this, fromIme);
425     }
426 
427     /**
428      * An internal-only version of performHapticFeedback that allows the caller access to the
429      * {@link HalVibration}.
430      * The Vibration is only returned if it is ongoing after this method returns.
431      */
432     @VisibleForTesting
433     @Nullable
performHapticFeedbackInternal( int uid, int deviceId, String opPkg, int constant, boolean always, String reason, IBinder token, boolean fromIme)434     HalVibration performHapticFeedbackInternal(
435             int uid, int deviceId, String opPkg, int constant, boolean always, String reason,
436             IBinder token, boolean fromIme) {
437         HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider();
438         if (hapticVibrationProvider == null) {
439             Slog.e(TAG, "performHapticFeedback; haptic vibration provider not ready.");
440             return null;
441         }
442         if (hapticVibrationProvider.isRestrictedHapticFeedback(constant)
443                 && !hasPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS)) {
444             Slog.w(TAG, "performHapticFeedback; no permission for system constant " + constant);
445             return null;
446         }
447         VibrationEffect effect = hapticVibrationProvider.getVibrationForHapticFeedback(constant);
448         if (effect == null) {
449             Slog.w(TAG, "performHapticFeedback; vibration absent for constant " + constant);
450             return null;
451         }
452         CombinedVibration vib = CombinedVibration.createParallel(effect);
453         VibrationAttributes attrs =
454                 hapticVibrationProvider.getVibrationAttributesForHapticFeedback(
455                         constant, /* bypassVibrationIntensitySetting= */ always, fromIme);
456         reason = "performHapticFeedback(constant=" + constant + "): " + reason;
457         VibratorFrameworkStatsLogger.logPerformHapticsFeedbackIfKeyboard(uid, constant);
458         return vibrateWithoutPermissionCheck(uid, deviceId, opPkg, vib, attrs, reason, token);
459     }
460 
461     /**
462      * An internal-only version of vibrate that allows the caller access to the
463      * {@link HalVibration}.
464      * The Vibration is only returned if it is ongoing after this method returns.
465      */
466     @VisibleForTesting
467     @Nullable
vibrateWithPermissionCheck(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs, String reason, IBinder token)468     HalVibration vibrateWithPermissionCheck(int uid, int deviceId, String opPkg,
469             @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs,
470             String reason, IBinder token) {
471         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
472         try {
473             attrs = fixupVibrationAttributes(attrs, effect);
474             mContext.enforceCallingOrSelfPermission(
475                     android.Manifest.permission.VIBRATE, "vibrate");
476             return vibrateInternal(uid, deviceId, opPkg, effect, attrs, reason, token);
477         } finally {
478             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
479         }
480     }
481 
vibrateWithoutPermissionCheck(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs, String reason, IBinder token)482     HalVibration vibrateWithoutPermissionCheck(int uid, int deviceId, String opPkg,
483             @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
484             String reason, IBinder token) {
485         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate no perm check, reason = " + reason);
486         try {
487             return vibrateInternal(uid, deviceId, opPkg, effect, attrs, reason, token);
488         } finally {
489             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
490         }
491     }
492 
vibrateInternal(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs, String reason, IBinder token)493     private HalVibration vibrateInternal(int uid, int deviceId, String opPkg,
494             @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
495             String reason, IBinder token) {
496         if (token == null) {
497             Slog.e(TAG, "token must not be null");
498             return null;
499         }
500         enforceUpdateAppOpsStatsPermission(uid);
501         if (!isEffectValid(effect)) {
502             return null;
503         }
504         // Create Vibration.Stats as close to the received request as possible, for tracking.
505         HalVibration vib = new HalVibration(token, effect,
506                 new Vibration.CallerInfo(attrs, uid, deviceId, opPkg, reason));
507         fillVibrationFallbacks(vib, effect);
508 
509         if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
510             // Force update of user settings before checking if this vibration effect should
511             // be ignored or scaled.
512             mVibrationSettings.update();
513         }
514 
515         synchronized (mLock) {
516             if (DEBUG) {
517                 Slog.d(TAG, "Starting vibrate for vibration " + vib.id);
518             }
519 
520             // Check if user settings or DnD is set to ignore this vibration.
521             Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked(vib.callerInfo);
522 
523             // Check if ongoing vibration is more important than this vibration.
524             if (vibrationEndInfo == null) {
525                 vibrationEndInfo = shouldIgnoreVibrationForOngoingLocked(vib);
526             }
527 
528             // If not ignored so far then try to start this vibration.
529             if (vibrationEndInfo == null) {
530                 final long ident = Binder.clearCallingIdentity();
531                 try {
532                     if (mCurrentExternalVibration != null) {
533                         mCurrentExternalVibration.mute();
534                         vib.stats.reportInterruptedAnotherVibration(
535                                 mCurrentExternalVibration.callerInfo);
536                         endExternalVibrateLocked(
537                                 new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
538                                         vib.callerInfo),
539                                 /* continueExternalControl= */ false);
540                     } else if (mCurrentVibration != null) {
541                         if (mCurrentVibration.getVibration().canPipelineWith(vib)) {
542                             // Don't cancel the current vibration if it's pipeline-able.
543                             // Note that if there is a pending next vibration that can't be
544                             // pipelined, it will have already cancelled the current one, so we
545                             // don't need to consider it here as well.
546                             if (DEBUG) {
547                                 Slog.d(TAG, "Pipelining vibration " + vib.id);
548                             }
549                         } else {
550                             vib.stats.reportInterruptedAnotherVibration(
551                                     mCurrentVibration.getVibration().callerInfo);
552                             mCurrentVibration.notifyCancelled(
553                                     new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
554                                             vib.callerInfo),
555                                     /* immediate= */ false);
556                         }
557                     }
558                     vibrationEndInfo = startVibrationLocked(vib);
559                 } finally {
560                     Binder.restoreCallingIdentity(ident);
561                 }
562             }
563 
564             // Ignored or failed to start the vibration, end it and report metrics right away.
565             if (vibrationEndInfo != null) {
566                 endVibrationLocked(vib, vibrationEndInfo, /* shouldWriteStats= */ true);
567             }
568             return vib;
569         }
570     }
571 
572     @Override // Binder call
cancelVibrate(int usageFilter, IBinder token)573     public void cancelVibrate(int usageFilter, IBinder token) {
574         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "cancelVibrate");
575         try {
576             mContext.enforceCallingOrSelfPermission(
577                     android.Manifest.permission.VIBRATE,
578                     "cancelVibrate");
579 
580             synchronized (mLock) {
581                 if (DEBUG) {
582                     Slog.d(TAG, "Canceling vibration");
583                 }
584                 Vibration.EndInfo cancelledByUserInfo =
585                         new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER);
586                 final long ident = Binder.clearCallingIdentity();
587                 try {
588                     if (mNextVibration != null
589                             && shouldCancelVibration(mNextVibration.getVibration(),
590                             usageFilter, token)) {
591                         clearNextVibrationLocked(cancelledByUserInfo);
592                     }
593                     if (mCurrentVibration != null
594                             && shouldCancelVibration(mCurrentVibration.getVibration(),
595                             usageFilter, token)) {
596                         mCurrentVibration.notifyCancelled(
597                                 cancelledByUserInfo, /* immediate= */false);
598                     }
599                     if (mCurrentExternalVibration != null
600                             && shouldCancelVibration(
601                             mCurrentExternalVibration.externalVibration.getVibrationAttributes(),
602                             usageFilter)) {
603                         mCurrentExternalVibration.mute();
604                         endExternalVibrateLocked(
605                                 cancelledByUserInfo, /* continueExternalControl= */ false);
606                     }
607                 } finally {
608                     Binder.restoreCallingIdentity(ident);
609                 }
610             }
611         } finally {
612             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
613         }
614     }
615 
616     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)617     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
618         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
619 
620         final long ident = Binder.clearCallingIdentity();
621 
622         boolean isDumpProto = false;
623         for (String arg : args) {
624             if (arg.equals("--proto")) {
625                 isDumpProto = true;
626             }
627         }
628         try {
629             if (isDumpProto) {
630                 dumpProto(fd);
631             } else {
632                 dumpText(pw);
633             }
634         } finally {
635             Binder.restoreCallingIdentity(ident);
636         }
637     }
638 
dumpText(PrintWriter w)639     private void dumpText(PrintWriter w) {
640         if (DEBUG) {
641             Slog.d(TAG, "Dumping vibrator manager service to text...");
642         }
643         IndentingPrintWriter pw = new IndentingPrintWriter(w, /* singleIndent= */ "  ");
644         synchronized (mLock) {
645             pw.println("VibratorManagerService:");
646             pw.increaseIndent();
647 
648             mVibrationSettings.dump(pw);
649             pw.println();
650 
651             mVibrationScaler.dump(pw);
652             pw.println();
653 
654             pw.println("Vibrators:");
655             pw.increaseIndent();
656             for (int i = 0; i < mVibrators.size(); i++) {
657                 mVibrators.valueAt(i).dump(pw);
658             }
659             pw.decreaseIndent();
660             pw.println();
661 
662             pw.println("CurrentVibration:");
663             pw.increaseIndent();
664             if (mCurrentVibration != null) {
665                 mCurrentVibration.getVibration().getDebugInfo().dump(pw);
666             } else {
667                 pw.println("null");
668             }
669             pw.decreaseIndent();
670             pw.println();
671 
672             pw.println("NextVibration:");
673             pw.increaseIndent();
674             if (mNextVibration != null) {
675                 mNextVibration.getVibration().getDebugInfo().dump(pw);
676             } else {
677                 pw.println("null");
678             }
679             pw.decreaseIndent();
680             pw.println();
681 
682             pw.println("CurrentExternalVibration:");
683             pw.increaseIndent();
684             if (mCurrentExternalVibration != null) {
685                 mCurrentExternalVibration.getDebugInfo().dump(pw);
686             } else {
687                 pw.println("null");
688             }
689             pw.decreaseIndent();
690         }
691 
692         pw.println();
693         pw.println();
694         mVibratorManagerRecords.dump(pw);
695 
696         pw.println();
697         pw.println();
698         mVibratorControlService.dump(pw);
699     }
700 
dumpProto(FileDescriptor fd)701     private void dumpProto(FileDescriptor fd) {
702         final ProtoOutputStream proto = new ProtoOutputStream(fd);
703         if (DEBUG) {
704             Slog.d(TAG, "Dumping vibrator manager service to proto...");
705         }
706         synchronized (mLock) {
707             mVibrationSettings.dump(proto);
708             mVibrationScaler.dump(proto);
709             if (mCurrentVibration != null) {
710                 mCurrentVibration.getVibration().getDebugInfo().dump(proto,
711                         VibratorManagerServiceDumpProto.CURRENT_VIBRATION);
712             }
713             if (mCurrentExternalVibration != null) {
714                 mCurrentExternalVibration.getDebugInfo().dump(proto,
715                         VibratorManagerServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
716             }
717 
718             boolean isVibrating = false;
719             boolean isUnderExternalControl = false;
720             for (int i = 0; i < mVibrators.size(); i++) {
721                 proto.write(VibratorManagerServiceDumpProto.VIBRATOR_IDS, mVibrators.keyAt(i));
722                 isVibrating |= mVibrators.valueAt(i).isVibrating();
723                 isUnderExternalControl |= mVibrators.valueAt(i).isUnderExternalControl();
724             }
725             proto.write(VibratorManagerServiceDumpProto.IS_VIBRATING, isVibrating);
726             proto.write(VibratorManagerServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
727                     isUnderExternalControl);
728         }
729         mVibratorManagerRecords.dump(proto);
730         mVibratorControlService.dump(proto);
731         proto.flush();
732     }
733 
734     @Override
onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback cb, ResultReceiver resultReceiver)735     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
736             String[] args, ShellCallback cb, ResultReceiver resultReceiver) {
737         new VibratorManagerShellCommand(cb.getShellCallbackBinder())
738                 .exec(this, in, out, err, args, cb, resultReceiver);
739     }
740 
741     @VisibleForTesting
updateServiceState()742     void updateServiceState() {
743         synchronized (mLock) {
744             if (DEBUG) {
745                 Slog.d(TAG, "Updating device state...");
746             }
747             boolean inputDevicesChanged = mInputDeviceDelegate.updateInputDeviceVibrators(
748                     mVibrationSettings.shouldVibrateInputDevices());
749 
750             for (int i = 0; i < mAlwaysOnEffects.size(); i++) {
751                 updateAlwaysOnLocked(mAlwaysOnEffects.valueAt(i));
752             }
753 
754             if (mCurrentVibration == null) {
755                 return;
756             }
757 
758             HalVibration vib = mCurrentVibration.getVibration();
759             Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked(vib.callerInfo);
760 
761             if (inputDevicesChanged || (vibrationEndInfo != null)) {
762                 if (DEBUG) {
763                     Slog.d(TAG, "Canceling vibration because settings changed: "
764                             + (inputDevicesChanged ? "input devices changed"
765                             : vibrationEndInfo.status));
766                 }
767                 mCurrentVibration.notifyCancelled(
768                         new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
769                         /* immediate= */ false);
770             }
771         }
772     }
773 
setExternalControl(boolean externalControl, VibrationStats vibrationStats)774     private void setExternalControl(boolean externalControl, VibrationStats vibrationStats) {
775         for (int i = 0; i < mVibrators.size(); i++) {
776             mVibrators.valueAt(i).setExternalControl(externalControl);
777             vibrationStats.reportSetExternalControl();
778         }
779     }
780 
781     @GuardedBy("mLock")
updateAlwaysOnLocked(AlwaysOnVibration vib)782     private void updateAlwaysOnLocked(AlwaysOnVibration vib) {
783         for (int i = 0; i < vib.effects.size(); i++) {
784             VibratorController vibrator = mVibrators.get(vib.effects.keyAt(i));
785             PrebakedSegment effect = vib.effects.valueAt(i);
786             if (vibrator == null) {
787                 continue;
788             }
789             Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked(vib.callerInfo);
790             if (vibrationEndInfo == null) {
791                 effect = mVibrationScaler.scale(effect, vib.callerInfo.attrs.getUsage());
792             } else {
793                 // Vibration should not run, use null effect to remove registered effect.
794                 effect = null;
795             }
796             vibrator.updateAlwaysOn(vib.alwaysOnId, effect);
797         }
798     }
799 
800     @GuardedBy("mLock")
801     @Nullable
startVibrationLocked(HalVibration vib)802     private Vibration.EndInfo startVibrationLocked(HalVibration vib) {
803         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
804         try {
805             if (mInputDeviceDelegate.isAvailable()) {
806                 return startVibrationOnInputDevicesLocked(vib);
807             }
808 
809             VibrationStepConductor conductor = createVibrationStepConductor(vib);
810 
811             if (mCurrentVibration == null) {
812                 return startVibrationOnThreadLocked(conductor);
813             }
814             // If there's already a vibration queued (waiting for the previous one to finish
815             // cancelling), end it cleanly and replace it with the new one.
816             // Note that we don't consider pipelining here, because new pipelined ones should
817             // replace pending non-executing pipelined ones anyway.
818             clearNextVibrationLocked(
819                     new Vibration.EndInfo(Vibration.Status.IGNORED_SUPERSEDED, vib.callerInfo));
820             mNextVibration = conductor;
821             return null;
822         } finally {
823             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
824         }
825     }
826 
827     @GuardedBy("mLock")
828     @Nullable
startVibrationOnThreadLocked(VibrationStepConductor conductor)829     private Vibration.EndInfo startVibrationOnThreadLocked(VibrationStepConductor conductor) {
830         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationThreadLocked");
831         try {
832             HalVibration vib = conductor.getVibration();
833             int mode = startAppOpModeLocked(vib.callerInfo);
834             switch (mode) {
835                 case AppOpsManager.MODE_ALLOWED:
836                     Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
837                     // Make sure mCurrentVibration is set while triggering the VibrationThread.
838                     mCurrentVibration = conductor;
839                     if (!mVibrationThread.runVibrationOnVibrationThread(mCurrentVibration)) {
840                         // Shouldn't happen. The method call already logs a wtf.
841                         mCurrentVibration = null;  // Aborted.
842                         return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_SCHEDULING);
843                     }
844                     return null;
845                 case AppOpsManager.MODE_ERRORED:
846                     Slog.w(TAG, "Start AppOpsManager operation errored for uid "
847                             + vib.callerInfo.uid);
848                     return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_APP_OPS);
849                 default:
850                     return new Vibration.EndInfo(Vibration.Status.IGNORED_APP_OPS);
851             }
852         } finally {
853             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
854         }
855     }
856 
857     @GuardedBy("mLock")
endVibrationLocked(HalVibration vib, Vibration.EndInfo vibrationEndInfo, boolean shouldWriteStats)858     private void endVibrationLocked(HalVibration vib, Vibration.EndInfo vibrationEndInfo,
859             boolean shouldWriteStats) {
860         vib.end(vibrationEndInfo);
861         logAndRecordVibration(vib.getDebugInfo());
862         if (shouldWriteStats) {
863             mFrameworkStatsLogger.writeVibrationReportedAsync(
864                     vib.getStatsInfo(/* completionUptimeMillis= */ SystemClock.uptimeMillis()));
865         }
866     }
867 
868     @GuardedBy("mLock")
endVibrationAndWriteStatsLocked(ExternalVibrationHolder vib, Vibration.EndInfo vibrationEndInfo)869     private void endVibrationAndWriteStatsLocked(ExternalVibrationHolder vib,
870             Vibration.EndInfo vibrationEndInfo) {
871         vib.end(vibrationEndInfo);
872         logAndRecordVibration(vib.getDebugInfo());
873         mFrameworkStatsLogger.writeVibrationReportedAsync(
874                 vib.getStatsInfo(/* completionUptimeMillis= */ SystemClock.uptimeMillis()));
875     }
876 
createVibrationStepConductor(HalVibration vib)877     private VibrationStepConductor createVibrationStepConductor(HalVibration vib) {
878         CompletableFuture<Void> requestVibrationParamsFuture = null;
879 
880         if (Flags.adaptiveHapticsEnabled() && !vib.callerInfo.attrs.isFlagSet(
881                 VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)
882                 && mVibratorControlService.shouldRequestVibrationParams(
883                 vib.callerInfo.attrs.getUsage())) {
884             requestVibrationParamsFuture =
885                     mVibratorControlService.triggerVibrationParamsRequest(
886                             vib.callerInfo.uid, vib.callerInfo.attrs.getUsage(),
887                             mVibrationSettings.getRequestVibrationParamsTimeoutMs());
888         }
889 
890         return new VibrationStepConductor(vib, mVibrationSettings, mDeviceAdapter, mVibrationScaler,
891                 mFrameworkStatsLogger, requestVibrationParamsFuture, mVibrationThreadCallbacks);
892     }
893 
startVibrationOnInputDevicesLocked(HalVibration vib)894     private Vibration.EndInfo startVibrationOnInputDevicesLocked(HalVibration vib) {
895         if (!vib.callerInfo.attrs.isFlagSet(
896                 VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)) {
897             // Scale resolves the default amplitudes from the effect before scaling them.
898             vib.scaleEffects(mVibrationScaler);
899         } else {
900             vib.resolveEffects(mVibrationScaler.getDefaultVibrationAmplitude());
901         }
902         mInputDeviceDelegate.vibrateIfAvailable(vib.callerInfo, vib.getEffectToPlay());
903 
904         return new Vibration.EndInfo(Vibration.Status.FORWARDED_TO_INPUT_DEVICES);
905     }
906 
logAndRecordVibration(Vibration.DebugInfo info)907     private void logAndRecordVibration(Vibration.DebugInfo info) {
908         info.logMetrics(mFrameworkStatsLogger);
909         logVibrationStatus(info.mCallerInfo.uid, info.mCallerInfo.attrs, info.mStatus);
910         mVibratorManagerRecords.record(info);
911     }
912 
logVibrationStatus(int uid, VibrationAttributes attrs, Vibration.Status status)913     private void logVibrationStatus(int uid, VibrationAttributes attrs,
914             Vibration.Status status) {
915         switch (status) {
916             case IGNORED_BACKGROUND:
917                 Slog.e(TAG, "Ignoring incoming vibration as process with"
918                         + " uid= " + uid + " is background," + " attrs= " + attrs);
919                 break;
920             case IGNORED_ERROR_APP_OPS:
921                 Slog.w(TAG, "Would be an error: vibrate from uid " + uid);
922                 break;
923             case IGNORED_FOR_EXTERNAL:
924                 if (DEBUG) {
925                     Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
926                 }
927                 break;
928             case IGNORED_FOR_HIGHER_IMPORTANCE:
929                 if (DEBUG) {
930                     Slog.d(TAG, "Ignoring incoming vibration in favor of ongoing vibration"
931                             + " with higher importance");
932                 }
933                 break;
934             case IGNORED_FOR_ONGOING:
935                 if (DEBUG) {
936                     Slog.d(TAG, "Ignoring incoming vibration in favor of repeating vibration");
937                 }
938                 break;
939             case IGNORED_FOR_RINGER_MODE:
940                 if (DEBUG) {
941                     Slog.d(TAG, "Ignoring incoming vibration because of ringer mode, attrs="
942                             + attrs);
943                 }
944                 break;
945             case IGNORED_FROM_VIRTUAL_DEVICE:
946                 if (DEBUG) {
947                     Slog.d(TAG, "Ignoring incoming vibration because it came from a virtual"
948                             + " device, attrs= " + attrs);
949                 }
950                 break;
951             default:
952                 if (DEBUG) {
953                     Slog.d(TAG, "Vibration for uid=" + uid + " and with attrs=" + attrs
954                             + " ended with status " + status);
955                 }
956         }
957     }
958 
959     @GuardedBy("mLock")
reportFinishedVibrationLocked(Vibration.EndInfo vibrationEndInfo)960     private void reportFinishedVibrationLocked(Vibration.EndInfo vibrationEndInfo) {
961         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
962         Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
963         try {
964             HalVibration vib = mCurrentVibration.getVibration();
965             if (DEBUG) {
966                 Slog.d(TAG, "Reporting vibration " + vib.id + " finished with "
967                         + vibrationEndInfo);
968             }
969             // DO NOT write metrics at this point, wait for the VibrationThread to report the
970             // vibration was released, after all cleanup. The metrics will be reported then.
971             endVibrationLocked(vib, vibrationEndInfo, /* shouldWriteStats= */ false);
972             finishAppOpModeLocked(vib.callerInfo);
973         } finally {
974             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
975         }
976     }
977 
onSyncedVibrationComplete(long vibrationId)978     private void onSyncedVibrationComplete(long vibrationId) {
979         synchronized (mLock) {
980             if (mCurrentVibration != null
981                     && mCurrentVibration.getVibration().id == vibrationId) {
982                 if (DEBUG) {
983                     Slog.d(TAG, "Synced vibration " + vibrationId + " complete, notifying thread");
984                 }
985                 mCurrentVibration.notifySyncedVibrationComplete();
986             }
987         }
988     }
989 
onVibrationComplete(int vibratorId, long vibrationId)990     private void onVibrationComplete(int vibratorId, long vibrationId) {
991         synchronized (mLock) {
992             if (mCurrentVibration != null
993                     && mCurrentVibration.getVibration().id == vibrationId) {
994                 if (DEBUG) {
995                     Slog.d(TAG, "Vibration " + vibrationId + " on vibrator " + vibratorId
996                             + " complete, notifying thread");
997                 }
998                 mCurrentVibration.notifyVibratorComplete(vibratorId);
999             }
1000         }
1001     }
1002 
1003     /**
1004      * Check if given vibration should be ignored by this service because of the ongoing vibration.
1005      *
1006      * @return a Vibration.EndInfo if the vibration should be ignored, null otherwise.
1007      */
1008     @GuardedBy("mLock")
1009     @Nullable
shouldIgnoreVibrationForOngoingLocked(Vibration vib)1010     private Vibration.EndInfo shouldIgnoreVibrationForOngoingLocked(Vibration vib) {
1011         if (mCurrentExternalVibration != null) {
1012             return shouldIgnoreVibrationForOngoing(vib, mCurrentExternalVibration);
1013         }
1014 
1015         if (mNextVibration != null) {
1016             Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationForOngoing(vib,
1017                     mNextVibration.getVibration());
1018             if (vibrationEndInfo != null) {
1019                 // Next vibration has higher importance than the new one, so the new vibration
1020                 // should be ignored.
1021                 return vibrationEndInfo;
1022             }
1023         }
1024 
1025         if (mCurrentVibration != null) {
1026             HalVibration currentVibration = mCurrentVibration.getVibration();
1027             if (currentVibration.hasEnded() || mCurrentVibration.wasNotifiedToCancel()) {
1028                 // Current vibration has ended or is cancelling, should not block incoming
1029                 // vibrations.
1030                 return null;
1031             }
1032 
1033             return shouldIgnoreVibrationForOngoing(vib, currentVibration);
1034         }
1035 
1036         return null;
1037     }
1038 
1039     /**
1040      * Checks if the ongoing vibration has higher importance than the new one. If they have similar
1041      * importance, then {@link Vibration#isRepeating()} is used as a tiebreaker.
1042      *
1043      * @return a Vibration.EndInfo if the vibration should be ignored, null otherwise.
1044      */
1045     @Nullable
shouldIgnoreVibrationForOngoing( @onNull Vibration newVibration, @NonNull Vibration ongoingVibration)1046     private static Vibration.EndInfo shouldIgnoreVibrationForOngoing(
1047             @NonNull Vibration newVibration, @NonNull Vibration ongoingVibration) {
1048 
1049         int newVibrationImportance = getVibrationImportance(newVibration);
1050         int ongoingVibrationImportance = getVibrationImportance(ongoingVibration);
1051 
1052         if (newVibrationImportance > ongoingVibrationImportance) {
1053             // New vibration has higher importance and should not be ignored.
1054             return null;
1055         }
1056 
1057         if (ongoingVibrationImportance > newVibrationImportance) {
1058             // Existing vibration has higher importance and should not be cancelled.
1059             return new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_HIGHER_IMPORTANCE,
1060                     ongoingVibration.callerInfo);
1061         }
1062 
1063         // Same importance, use repeating as a tiebreaker.
1064         if (ongoingVibration.isRepeating() && !newVibration.isRepeating()) {
1065             // Ongoing vibration is repeating and new one is not, give priority to ongoing
1066             return new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_ONGOING,
1067                     ongoingVibration.callerInfo);
1068         }
1069         // New vibration is repeating or this is a complete tie between them,
1070         // give priority to new vibration.
1071         return null;
1072     }
1073 
1074     /**
1075      * Gets the vibration importance based on usage. In the case where usage is unknown, it maps
1076      * repeating vibrations to ringtones and non-repeating vibrations to touches.
1077      *
1078      * @return a numeric representation for the vibration importance, larger values represent a
1079      * higher importance
1080      */
getVibrationImportance(Vibration vibration)1081     private static int getVibrationImportance(Vibration vibration) {
1082         int usage = vibration.callerInfo.attrs.getUsage();
1083         if (usage == VibrationAttributes.USAGE_UNKNOWN) {
1084             if (vibration.isRepeating()) {
1085                 usage = VibrationAttributes.USAGE_RINGTONE;
1086             } else {
1087                 usage = VibrationAttributes.USAGE_TOUCH;
1088             }
1089         }
1090 
1091         switch (usage) {
1092             case VibrationAttributes.USAGE_RINGTONE:
1093                 return 5;
1094             case VibrationAttributes.USAGE_ALARM:
1095                 return 4;
1096             case VibrationAttributes.USAGE_NOTIFICATION:
1097                 return 3;
1098             case VibrationAttributes.USAGE_COMMUNICATION_REQUEST:
1099             case VibrationAttributes.USAGE_ACCESSIBILITY:
1100                 return 2;
1101             case VibrationAttributes.USAGE_HARDWARE_FEEDBACK:
1102             case VibrationAttributes.USAGE_PHYSICAL_EMULATION:
1103                 return 1;
1104             case VibrationAttributes.USAGE_MEDIA:
1105             case VibrationAttributes.USAGE_TOUCH:
1106             default:
1107                 return 0;
1108         }
1109     }
1110 
1111     /**
1112      * Check if given vibration should be ignored by this service.
1113      *
1114      * @return a Vibration.EndInfo if the vibration should be ignored, null otherwise.
1115      */
1116     @GuardedBy("mLock")
1117     @Nullable
shouldIgnoreVibrationLocked(Vibration.CallerInfo callerInfo)1118     private Vibration.EndInfo shouldIgnoreVibrationLocked(Vibration.CallerInfo callerInfo) {
1119         Vibration.Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(callerInfo);
1120         if (statusFromSettings != null) {
1121             return new Vibration.EndInfo(statusFromSettings);
1122         }
1123 
1124         int mode = checkAppOpModeLocked(callerInfo);
1125         if (mode != AppOpsManager.MODE_ALLOWED) {
1126             if (mode == AppOpsManager.MODE_ERRORED) {
1127                 // We might be getting calls from within system_server, so we don't actually
1128                 // want to throw a SecurityException here.
1129                 return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_APP_OPS);
1130             } else {
1131                 return new Vibration.EndInfo(Vibration.Status.IGNORED_APP_OPS);
1132             }
1133         }
1134 
1135         return null;
1136     }
1137 
1138     /**
1139      * Return true if the vibration has the same token and usage belongs to given usage class.
1140      *
1141      * @param vib         The ongoing or pending vibration to be cancelled.
1142      * @param usageFilter The vibration usages to be cancelled, any bitwise combination of
1143      *                    VibrationAttributes.USAGE_* values.
1144      * @param token       The binder token to identify the vibration origin. Only vibrations
1145      *                    started with the same token can be cancelled with it.
1146      */
shouldCancelVibration(HalVibration vib, int usageFilter, IBinder token)1147     private boolean shouldCancelVibration(HalVibration vib, int usageFilter, IBinder token) {
1148         return (vib.callerToken == token) && shouldCancelVibration(vib.callerInfo.attrs,
1149                 usageFilter);
1150     }
1151 
1152     /**
1153      * Return true if the external vibration usage belongs to given usage class.
1154      *
1155      * @param attrs       The attributes of an ongoing or pending vibration to be cancelled.
1156      * @param usageFilter The vibration usages to be cancelled, any bitwise combination of
1157      *                    VibrationAttributes.USAGE_* values.
1158      */
shouldCancelVibration(VibrationAttributes attrs, int usageFilter)1159     private boolean shouldCancelVibration(VibrationAttributes attrs, int usageFilter) {
1160         if (attrs.getUsage() == VibrationAttributes.USAGE_UNKNOWN) {
1161             // Special case, usage UNKNOWN would match all filters. Instead it should only match if
1162             // it's cancelling that usage specifically, or if cancelling all usages.
1163             return usageFilter == VibrationAttributes.USAGE_UNKNOWN
1164                     || usageFilter == VibrationAttributes.USAGE_FILTER_MATCH_ALL;
1165         }
1166         return (usageFilter & attrs.getUsage()) == attrs.getUsage();
1167     }
1168 
1169     /**
1170      * Check which mode should be set for a vibration with given {@code uid}, {@code opPkg} and
1171      * {@code attrs}. This will return one of the AppOpsManager.MODE_*.
1172      */
1173     @GuardedBy("mLock")
checkAppOpModeLocked(Vibration.CallerInfo callerInfo)1174     private int checkAppOpModeLocked(Vibration.CallerInfo callerInfo) {
1175         int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
1176                 callerInfo.attrs.getAudioUsage(), callerInfo.uid, callerInfo.opPkg);
1177         int fixedMode = fixupAppOpModeLocked(mode, callerInfo.attrs);
1178         if (mode != fixedMode && fixedMode == AppOpsManager.MODE_ALLOWED) {
1179             // If we're just ignoring the vibration op then this is set by DND and we should ignore
1180             // if we're asked to bypass. AppOps won't be able to record this operation, so make
1181             // sure we at least note it in the logs for debugging.
1182             Slog.d(TAG, "Bypassing DND for vibrate from uid " + callerInfo.uid);
1183         }
1184         return fixedMode;
1185     }
1186 
1187     /** Start an operation in {@link AppOpsManager}, if allowed. */
1188     @GuardedBy("mLock")
startAppOpModeLocked(Vibration.CallerInfo callerInfo)1189     private int startAppOpModeLocked(Vibration.CallerInfo callerInfo) {
1190         return fixupAppOpModeLocked(
1191                 mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg),
1192                 callerInfo.attrs);
1193     }
1194 
1195     /**
1196      * Finish a previously started operation in {@link AppOpsManager}. This will be a noop if no
1197      * operation with same uid was previously started.
1198      */
1199     @GuardedBy("mLock")
finishAppOpModeLocked(Vibration.CallerInfo callerInfo)1200     private void finishAppOpModeLocked(Vibration.CallerInfo callerInfo) {
1201         mAppOps.finishOp(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg);
1202     }
1203 
1204     /**
1205      * Enforces {@link android.Manifest.permission#UPDATE_APP_OPS_STATS} to incoming UID if it's
1206      * different from the calling UID.
1207      */
enforceUpdateAppOpsStatsPermission(int uid)1208     private void enforceUpdateAppOpsStatsPermission(int uid) {
1209         if (uid == Binder.getCallingUid()) {
1210             return;
1211         }
1212         if (Binder.getCallingPid() == Process.myPid()) {
1213             return;
1214         }
1215         mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
1216                 Binder.getCallingPid(), Binder.getCallingUid(), null);
1217     }
1218 
1219     /**
1220      * Validate the incoming {@link CombinedVibration}.
1221      *
1222      * We can't throw exceptions here since we might be called from some system_server component,
1223      * which would bring the whole system down.
1224      *
1225      * @return whether the CombinedVibrationEffect is non-null and valid
1226      */
isEffectValid(@ullable CombinedVibration effect)1227     private static boolean isEffectValid(@Nullable CombinedVibration effect) {
1228         if (effect == null) {
1229             Slog.wtf(TAG, "effect must not be null");
1230             return false;
1231         }
1232         try {
1233             effect.validate();
1234         } catch (Exception e) {
1235             Slog.wtf(TAG, "Encountered issue when verifying CombinedVibrationEffect.", e);
1236             return false;
1237         }
1238         return true;
1239     }
1240 
1241     /**
1242      * Sets fallback effects to all prebaked ones in given combination of effects, based on {@link
1243      * VibrationSettings#getFallbackEffect}.
1244      */
fillVibrationFallbacks(HalVibration vib, CombinedVibration effect)1245     private void fillVibrationFallbacks(HalVibration vib, CombinedVibration effect) {
1246         if (effect instanceof CombinedVibration.Mono) {
1247             fillVibrationFallbacks(vib, ((CombinedVibration.Mono) effect).getEffect());
1248         } else if (effect instanceof CombinedVibration.Stereo) {
1249             SparseArray<VibrationEffect> effects =
1250                     ((CombinedVibration.Stereo) effect).getEffects();
1251             for (int i = 0; i < effects.size(); i++) {
1252                 fillVibrationFallbacks(vib, effects.valueAt(i));
1253             }
1254         } else if (effect instanceof CombinedVibration.Sequential) {
1255             List<CombinedVibration> effects =
1256                     ((CombinedVibration.Sequential) effect).getEffects();
1257             for (int i = 0; i < effects.size(); i++) {
1258                 fillVibrationFallbacks(vib, effects.get(i));
1259             }
1260         }
1261     }
1262 
fillVibrationFallbacks(HalVibration vib, VibrationEffect effect)1263     private void fillVibrationFallbacks(HalVibration vib, VibrationEffect effect) {
1264         VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
1265         int segmentCount = composed.getSegments().size();
1266         for (int i = 0; i < segmentCount; i++) {
1267             VibrationEffectSegment segment = composed.getSegments().get(i);
1268             if (segment instanceof PrebakedSegment) {
1269                 PrebakedSegment prebaked = (PrebakedSegment) segment;
1270                 VibrationEffect fallback = mVibrationSettings.getFallbackEffect(
1271                         prebaked.getEffectId());
1272                 if (prebaked.shouldFallback() && fallback != null) {
1273                     vib.addFallback(prebaked.getEffectId(), fallback);
1274                 }
1275             }
1276         }
1277     }
1278 
1279     /**
1280      * Return new {@link VibrationAttributes} that only applies flags that this user has permissions
1281      * to use.
1282      */
1283     @NonNull
fixupVibrationAttributes(@ullable VibrationAttributes attrs, @Nullable CombinedVibration effect)1284     private VibrationAttributes fixupVibrationAttributes(@Nullable VibrationAttributes attrs,
1285             @Nullable CombinedVibration effect) {
1286         if (attrs == null) {
1287             attrs = DEFAULT_ATTRIBUTES;
1288         }
1289         int usage = attrs.getUsage();
1290         if ((usage == VibrationAttributes.USAGE_UNKNOWN)
1291                 && (effect != null) && effect.isHapticFeedbackCandidate()) {
1292             usage = VibrationAttributes.USAGE_TOUCH;
1293         }
1294         int flags = attrs.getFlags();
1295         if ((flags & ATTRIBUTES_ALL_BYPASS_FLAGS) != 0) {
1296             if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
1297                     || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
1298                     || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
1299                 // Remove bypass flags from attributes if the app does not have permissions.
1300                 flags &= ~ATTRIBUTES_ALL_BYPASS_FLAGS;
1301             }
1302         }
1303         if ((usage == attrs.getUsage()) && (flags == attrs.getFlags())) {
1304             return attrs;
1305         }
1306         return new VibrationAttributes.Builder(attrs)
1307                 .setUsage(usage)
1308                 .setFlags(flags, attrs.getFlags())
1309                 .build();
1310     }
1311 
1312     @GuardedBy("mLock")
1313     @Nullable
fixupAlwaysOnEffectsLocked( CombinedVibration effect)1314     private SparseArray<PrebakedSegment> fixupAlwaysOnEffectsLocked(
1315             CombinedVibration effect) {
1316         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "fixupAlwaysOnEffectsLocked");
1317         try {
1318             SparseArray<VibrationEffect> effects;
1319             if (effect instanceof CombinedVibration.Mono) {
1320                 VibrationEffect syncedEffect = ((CombinedVibration.Mono) effect).getEffect();
1321                 effects = transformAllVibratorsLocked(unused -> syncedEffect);
1322             } else if (effect instanceof CombinedVibration.Stereo) {
1323                 effects = ((CombinedVibration.Stereo) effect).getEffects();
1324             } else {
1325                 // Only synced combinations can be used for always-on effects.
1326                 return null;
1327             }
1328             SparseArray<PrebakedSegment> result = new SparseArray<>();
1329             for (int i = 0; i < effects.size(); i++) {
1330                 PrebakedSegment prebaked = extractPrebakedSegment(effects.valueAt(i));
1331                 if (prebaked == null) {
1332                     Slog.e(TAG, "Only prebaked effects supported for always-on.");
1333                     return null;
1334                 }
1335                 int vibratorId = effects.keyAt(i);
1336                 VibratorController vibrator = mVibrators.get(vibratorId);
1337                 if (vibrator != null && vibrator.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
1338                     result.put(vibratorId, prebaked);
1339                 }
1340             }
1341             if (result.size() == 0) {
1342                 return null;
1343             }
1344             return result;
1345         } finally {
1346             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1347         }
1348     }
1349 
1350     @Nullable
extractPrebakedSegment(VibrationEffect effect)1351     private static PrebakedSegment extractPrebakedSegment(VibrationEffect effect) {
1352         if (effect instanceof VibrationEffect.Composed) {
1353             VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
1354             if (composed.getSegments().size() == 1) {
1355                 VibrationEffectSegment segment = composed.getSegments().get(0);
1356                 if (segment instanceof PrebakedSegment) {
1357                     return (PrebakedSegment) segment;
1358                 }
1359             }
1360         }
1361         return null;
1362     }
1363 
1364     /**
1365      * Check given mode, one of the AppOpsManager.MODE_*, against {@link VibrationAttributes} to
1366      * allow bypassing {@link AppOpsManager} checks.
1367      */
1368     @GuardedBy("mLock")
fixupAppOpModeLocked(int mode, VibrationAttributes attrs)1369     private int fixupAppOpModeLocked(int mode, VibrationAttributes attrs) {
1370         if (mode == AppOpsManager.MODE_IGNORED
1371                 && attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
1372             return AppOpsManager.MODE_ALLOWED;
1373         }
1374         return mode;
1375     }
1376 
hasPermission(String permission)1377     private boolean hasPermission(String permission) {
1378         return mContext.checkCallingOrSelfPermission(permission)
1379                 == PackageManager.PERMISSION_GRANTED;
1380     }
1381 
1382     @GuardedBy("mLock")
shouldCancelOnScreenOffLocked(@ullable VibrationStepConductor conductor)1383     private boolean shouldCancelOnScreenOffLocked(@Nullable VibrationStepConductor conductor) {
1384         if (conductor == null) {
1385             return false;
1386         }
1387         HalVibration vib = conductor.getVibration();
1388         return mVibrationSettings.shouldCancelVibrationOnScreenOff(vib.callerInfo,
1389                 vib.stats.getCreateUptimeMillis());
1390     }
1391 
1392     @GuardedBy("mLock")
onAllVibratorsLocked(Consumer<VibratorController> consumer)1393     private void onAllVibratorsLocked(Consumer<VibratorController> consumer) {
1394         for (int i = 0; i < mVibrators.size(); i++) {
1395             consumer.accept(mVibrators.valueAt(i));
1396         }
1397     }
1398 
1399     @GuardedBy("mLock")
transformAllVibratorsLocked(Function<VibratorController, T> fn)1400     private <T> SparseArray<T> transformAllVibratorsLocked(Function<VibratorController, T> fn) {
1401         SparseArray<T> ret = new SparseArray<>(mVibrators.size());
1402         for (int i = 0; i < mVibrators.size(); i++) {
1403             ret.put(mVibrators.keyAt(i), fn.apply(mVibrators.valueAt(i)));
1404         }
1405         return ret;
1406     }
1407 
1408     /** Point of injection for test dependencies */
1409     @VisibleForTesting
1410     static class Injector {
1411 
getNativeWrapper()1412         NativeWrapper getNativeWrapper() {
1413             return new NativeWrapper();
1414         }
1415 
createHandler(Looper looper)1416         Handler createHandler(Looper looper) {
1417             return new Handler(looper);
1418         }
1419 
getBatteryStatsService()1420         IBatteryStats getBatteryStatsService() {
1421             return IBatteryStats.Stub.asInterface(ServiceManager.getService(
1422                     BatteryStats.SERVICE_NAME));
1423         }
1424 
getFrameworkStatsLogger(Handler handler)1425         VibratorFrameworkStatsLogger getFrameworkStatsLogger(Handler handler) {
1426             return new VibratorFrameworkStatsLogger(handler);
1427         }
1428 
createVibratorController(int vibratorId, VibratorController.OnVibrationCompleteListener listener)1429         VibratorController createVibratorController(int vibratorId,
1430                 VibratorController.OnVibrationCompleteListener listener) {
1431             return new VibratorController(vibratorId, listener);
1432         }
1433 
createHapticFeedbackVibrationProvider( Resources resources, VibratorInfo vibratorInfo)1434         HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider(
1435                 Resources resources, VibratorInfo vibratorInfo) {
1436             return new HapticFeedbackVibrationProvider(resources, vibratorInfo);
1437         }
1438 
addService(String name, IBinder service)1439         void addService(String name, IBinder service) {
1440             ServiceManager.addService(name, service);
1441         }
1442 
createVibratorControllerHolder()1443         VibratorControllerHolder createVibratorControllerHolder() {
1444             return new VibratorControllerHolder();
1445         }
1446 
isServiceDeclared(String name)1447         boolean isServiceDeclared(String name) {
1448             return ServiceManager.isDeclared(name);
1449         }
1450     }
1451 
1452     /**
1453      * Implementation of {@link VibrationThread.VibratorManagerHooks} that controls synced
1454      * vibrations and reports them when finished.
1455      */
1456     private final class VibrationThreadCallbacks implements VibrationThread.VibratorManagerHooks {
1457 
1458         @Override
prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds)1459         public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) {
1460             if ((mCapabilities & requiredCapabilities) != requiredCapabilities) {
1461                 // This sync step requires capabilities this device doesn't have, skipping sync...
1462                 return false;
1463             }
1464             return mNativeWrapper.prepareSynced(vibratorIds);
1465         }
1466 
1467         @Override
triggerSyncedVibration(long vibrationId)1468         public boolean triggerSyncedVibration(long vibrationId) {
1469             return mNativeWrapper.triggerSynced(vibrationId);
1470         }
1471 
1472         @Override
cancelSyncedVibration()1473         public void cancelSyncedVibration() {
1474             mNativeWrapper.cancelSynced();
1475         }
1476 
1477         @Override
noteVibratorOn(int uid, long duration)1478         public void noteVibratorOn(int uid, long duration) {
1479             try {
1480                 if (duration <= 0) {
1481                     // Tried to turn vibrator ON and got:
1482                     // duration == 0: Unsupported effect/method or zero-amplitude segment.
1483                     // duration < 0: Unexpected error triggering the vibrator.
1484                     // Skip battery stats and atom metric for VibratorStageChanged to ON.
1485                     return;
1486                 }
1487                 if (duration == Long.MAX_VALUE) {
1488                     // Repeating duration has started. Report a fixed duration here, noteVibratorOff
1489                     // should be called when this is cancelled.
1490                     duration = BATTERY_STATS_REPEATING_VIBRATION_DURATION;
1491                 }
1492                 mBatteryStatsService.noteVibratorOn(uid, duration);
1493                 mFrameworkStatsLogger.writeVibratorStateOnAsync(uid, duration);
1494             } catch (RemoteException e) {
1495                 Slog.e(TAG, "Error logging VibratorStateChanged to ON", e);
1496             }
1497         }
1498 
1499         @Override
noteVibratorOff(int uid)1500         public void noteVibratorOff(int uid) {
1501             try {
1502                 mBatteryStatsService.noteVibratorOff(uid);
1503                 mFrameworkStatsLogger.writeVibratorStateOffAsync(uid);
1504             } catch (RemoteException e) {
1505                 Slog.e(TAG, "Error logging VibratorStateChanged to OFF", e);
1506             }
1507         }
1508 
1509         @Override
onVibrationCompleted(long vibrationId, Vibration.EndInfo vibrationEndInfo)1510         public void onVibrationCompleted(long vibrationId, Vibration.EndInfo vibrationEndInfo) {
1511             if (DEBUG) {
1512                 Slog.d(TAG, "Vibration " + vibrationId + " finished with " + vibrationEndInfo);
1513             }
1514             synchronized (mLock) {
1515                 if (mCurrentVibration != null
1516                         && mCurrentVibration.getVibration().id == vibrationId) {
1517                     reportFinishedVibrationLocked(vibrationEndInfo);
1518                 }
1519             }
1520         }
1521 
1522         @Override
onVibrationThreadReleased(long vibrationId)1523         public void onVibrationThreadReleased(long vibrationId) {
1524             if (DEBUG) {
1525                 Slog.d(TAG, "VibrationThread released after finished vibration");
1526             }
1527             synchronized (mLock) {
1528                 if (DEBUG) {
1529                     Slog.d(TAG, "Processing VibrationThread released callback");
1530                 }
1531                 if (Build.IS_DEBUGGABLE && mCurrentVibration != null
1532                         && mCurrentVibration.getVibration().id != vibrationId) {
1533                     Slog.wtf(TAG, TextUtils.formatSimple(
1534                             "VibrationId mismatch on release. expected=%d, released=%d",
1535                             mCurrentVibration.getVibration().id, vibrationId));
1536                 }
1537                 if (mCurrentVibration != null) {
1538                     // This is when we consider the current vibration complete, so report metrics.
1539                     mFrameworkStatsLogger.writeVibrationReportedAsync(
1540                             mCurrentVibration.getVibration().getStatsInfo(
1541                                     /* completionUptimeMillis= */ SystemClock.uptimeMillis()));
1542                     mCurrentVibration = null;
1543                 }
1544                 if (mNextVibration != null) {
1545                     VibrationStepConductor nextConductor = mNextVibration;
1546                     mNextVibration = null;
1547                     Vibration.EndInfo vibrationEndInfo = startVibrationOnThreadLocked(
1548                             nextConductor);
1549                     if (vibrationEndInfo != null) {
1550                         // Failed to start the vibration, end it and report metrics right away.
1551                         endVibrationLocked(nextConductor.getVibration(),
1552                                 vibrationEndInfo, /* shouldWriteStats= */ true);
1553                     }
1554                 }
1555             }
1556         }
1557     }
1558 
1559     /** Listener for synced vibration completion callbacks from native. */
1560     @VisibleForTesting
1561     interface OnSyncedVibrationCompleteListener {
1562 
1563         /** Callback triggered when synced vibration is complete. */
onComplete(long vibrationId)1564         void onComplete(long vibrationId);
1565     }
1566 
1567     /**
1568      * Implementation of listeners to native vibrators with a weak reference to this service.
1569      */
1570     private static final class VibrationCompleteListener implements
1571             VibratorController.OnVibrationCompleteListener, OnSyncedVibrationCompleteListener {
1572         private WeakReference<VibratorManagerService> mServiceRef;
1573 
VibrationCompleteListener(VibratorManagerService service)1574         VibrationCompleteListener(VibratorManagerService service) {
1575             mServiceRef = new WeakReference<>(service);
1576         }
1577 
1578         @Override
onComplete(long vibrationId)1579         public void onComplete(long vibrationId) {
1580             VibratorManagerService service = mServiceRef.get();
1581             if (service != null) {
1582                 service.onSyncedVibrationComplete(vibrationId);
1583             }
1584         }
1585 
1586         @Override
onComplete(int vibratorId, long vibrationId)1587         public void onComplete(int vibratorId, long vibrationId) {
1588             VibratorManagerService service = mServiceRef.get();
1589             if (service != null) {
1590                 service.onVibrationComplete(vibratorId, vibrationId);
1591             }
1592         }
1593     }
1594 
1595     /**
1596      * Combination of prekabed vibrations on multiple vibrators, with the same {@link
1597      * VibrationAttributes}, that can be set for always-on effects.
1598      */
1599     private static final class AlwaysOnVibration {
1600         public final int alwaysOnId;
1601         public final Vibration.CallerInfo callerInfo;
1602         public final SparseArray<PrebakedSegment> effects;
1603 
AlwaysOnVibration(int alwaysOnId, Vibration.CallerInfo callerInfo, SparseArray<PrebakedSegment> effects)1604         AlwaysOnVibration(int alwaysOnId, Vibration.CallerInfo callerInfo,
1605                 SparseArray<PrebakedSegment> effects) {
1606             this.alwaysOnId = alwaysOnId;
1607             this.callerInfo = callerInfo;
1608             this.effects = effects;
1609         }
1610     }
1611 
1612     /** Holder for a {@link ExternalVibration}. */
1613     private final class ExternalVibrationHolder extends Vibration implements
1614             IBinder.DeathRecipient {
1615 
1616         public final ExternalVibration externalVibration;
1617         public ExternalVibrationScale scale = new ExternalVibrationScale();
1618 
1619         private Vibration.Status mStatus;
1620 
ExternalVibrationHolder(ExternalVibration externalVibration)1621         private ExternalVibrationHolder(ExternalVibration externalVibration) {
1622             super(externalVibration.getToken(), new Vibration.CallerInfo(
1623                     externalVibration.getVibrationAttributes(), externalVibration.getUid(),
1624                     // TODO(b/249785241): Find a way to link ExternalVibration to a VirtualDevice
1625                     // instead of using DEVICE_ID_INVALID here and relying on the UID checks.
1626                     Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null));
1627             this.externalVibration = externalVibration;
1628             mStatus = Vibration.Status.RUNNING;
1629         }
1630 
scale(VibrationScaler scaler, int usage)1631         public void scale(VibrationScaler scaler, int usage) {
1632             scale.scaleLevel = scaler.getScaleLevel(usage);
1633             scale.adaptiveHapticsScale = scaler.getAdaptiveHapticsScale(usage);
1634             stats.reportAdaptiveScale(scale.adaptiveHapticsScale);
1635         }
1636 
mute()1637         public void mute() {
1638             externalVibration.mute();
1639         }
1640 
linkToDeath()1641         public void linkToDeath() {
1642             externalVibration.linkToDeath(this);
1643         }
1644 
unlinkToDeath()1645         public void unlinkToDeath() {
1646             externalVibration.unlinkToDeath(this);
1647         }
1648 
isHoldingSameVibration(ExternalVibration externalVibration)1649         public boolean isHoldingSameVibration(ExternalVibration externalVibration) {
1650             return this.externalVibration.equals(externalVibration);
1651         }
1652 
end(Vibration.EndInfo info)1653         public void end(Vibration.EndInfo info) {
1654             if (mStatus != Vibration.Status.RUNNING) {
1655                 // Already ended, ignore this call
1656                 return;
1657             }
1658             mStatus = info.status;
1659             stats.reportEnded(info.endedBy);
1660 
1661             if (stats.hasStarted()) {
1662                 // External vibration doesn't have feedback from total time the vibrator was playing
1663                 // with non-zero amplitude, so we use the duration between start and end times of
1664                 // the vibration as the time the vibrator was ON, since the haptic channels are
1665                 // open for this duration and can receive vibration waveform data.
1666                 stats.reportVibratorOn(
1667                         stats.getEndUptimeMillis() - stats.getStartUptimeMillis());
1668             }
1669         }
1670 
binderDied()1671         public void binderDied() {
1672             synchronized (mLock) {
1673                 if (mCurrentExternalVibration != null) {
1674                     if (DEBUG) {
1675                         Slog.d(TAG, "External vibration finished because binder died");
1676                     }
1677                     endExternalVibrateLocked(
1678                             new Vibration.EndInfo(Vibration.Status.CANCELLED_BINDER_DIED),
1679                             /* continueExternalControl= */ false);
1680                 }
1681             }
1682         }
1683 
getDebugInfo()1684         public Vibration.DebugInfo getDebugInfo() {
1685             return new Vibration.DebugInfo(mStatus, stats, /* playedEffect= */ null,
1686                     /* originalEffect= */ null, scale.scaleLevel, scale.adaptiveHapticsScale,
1687                     callerInfo);
1688         }
1689 
getStatsInfo(long completionUptimeMillis)1690         public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
1691             return new VibrationStats.StatsInfo(
1692                     externalVibration.getUid(),
1693                     FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL,
1694                     externalVibration.getVibrationAttributes().getUsage(), mStatus, stats,
1695                     completionUptimeMillis);
1696         }
1697 
1698         @Override
isRepeating()1699         boolean isRepeating() {
1700             // We don't currently know if the external vibration is repeating, so we just use a
1701             // heuristic based on the usage. Ideally this would be propagated in the
1702             // ExternalVibration.
1703             int usage = externalVibration.getVibrationAttributes().getUsage();
1704             return usage == VibrationAttributes.USAGE_RINGTONE
1705                     || usage == VibrationAttributes.USAGE_ALARM;
1706         }
1707     }
1708 
1709     /** Wrapper around the static-native methods of {@link VibratorManagerService} for tests. */
1710     @VisibleForTesting
1711     public static class NativeWrapper {
1712 
1713         private long mNativeServicePtr = 0;
1714 
1715         /** Returns native pointer to newly created controller and connects with HAL service. */
init(OnSyncedVibrationCompleteListener listener)1716         public void init(OnSyncedVibrationCompleteListener listener) {
1717             mNativeServicePtr = nativeInit(listener);
1718             long finalizerPtr = nativeGetFinalizer();
1719 
1720             if (finalizerPtr != 0) {
1721                 NativeAllocationRegistry registry =
1722                         NativeAllocationRegistry.createMalloced(
1723                                 VibratorManagerService.class.getClassLoader(), finalizerPtr);
1724                 registry.registerNativeAllocation(this, mNativeServicePtr);
1725             }
1726         }
1727 
1728         /** Returns manager capabilities. */
getCapabilities()1729         public long getCapabilities() {
1730             return nativeGetCapabilities(mNativeServicePtr);
1731         }
1732 
1733         /** Returns vibrator ids. */
getVibratorIds()1734         public int[] getVibratorIds() {
1735             return nativeGetVibratorIds(mNativeServicePtr);
1736         }
1737 
1738         /** Prepare vibrators for triggering vibrations in sync. */
prepareSynced(@onNull int[] vibratorIds)1739         public boolean prepareSynced(@NonNull int[] vibratorIds) {
1740             return nativePrepareSynced(mNativeServicePtr, vibratorIds);
1741         }
1742 
1743         /** Trigger prepared synced vibration. */
triggerSynced(long vibrationId)1744         public boolean triggerSynced(long vibrationId) {
1745             return nativeTriggerSynced(mNativeServicePtr, vibrationId);
1746         }
1747 
1748         /** Cancel prepared synced vibration. */
cancelSynced()1749         public void cancelSynced() {
1750             nativeCancelSynced(mNativeServicePtr);
1751         }
1752     }
1753 
1754     /** Keep records of vibrations played and provide debug information for this service. */
1755     private static final class VibratorManagerRecords {
1756         private final VibrationRecords mAggregatedVibrationHistory;
1757         private final VibrationRecords mRecentVibrations;
1758 
VibratorManagerRecords(int recentVibrationSizeLimit, int aggregationSizeLimit, int aggregationTimeLimit)1759         VibratorManagerRecords(int recentVibrationSizeLimit, int aggregationSizeLimit,
1760                 int aggregationTimeLimit) {
1761             mAggregatedVibrationHistory =
1762                     new VibrationRecords(aggregationSizeLimit, aggregationTimeLimit);
1763             // Recent vibrations are not aggregated, to help debugging issues that just happened.
1764             mRecentVibrations =
1765                     new VibrationRecords(recentVibrationSizeLimit, /* aggregationTimeLimit= */ 0);
1766         }
1767 
record(Vibration.DebugInfo info)1768         synchronized void record(Vibration.DebugInfo info) {
1769             GroupedAggregatedLogRecords.AggregatedLogRecord<VibrationRecord> droppedRecord =
1770                     mRecentVibrations.add(new VibrationRecord(info));
1771             if (droppedRecord != null) {
1772                 // Move dropped record from recent list to aggregated history list.
1773                 mAggregatedVibrationHistory.add(droppedRecord.getLatest());
1774             }
1775         }
1776 
dump(IndentingPrintWriter pw)1777         synchronized void dump(IndentingPrintWriter pw) {
1778             pw.println("Recent vibrations:");
1779             pw.increaseIndent();
1780             mRecentVibrations.dump(pw);
1781             pw.decreaseIndent();
1782 
1783             pw.println();
1784             pw.println();
1785             pw.println("Aggregated vibration history:");
1786             pw.increaseIndent();
1787             mAggregatedVibrationHistory.dump(pw);
1788             pw.decreaseIndent();
1789         }
1790 
dump(ProtoOutputStream proto)1791         synchronized void dump(ProtoOutputStream proto) {
1792             mRecentVibrations.dump(proto);
1793         }
1794     }
1795 
1796     /** Keep records of vibrations played and provide debug information for this service. */
1797     private static final class VibrationRecords
1798             extends GroupedAggregatedLogRecords<VibrationRecord> {
1799 
VibrationRecords(int sizeLimit, int aggregationTimeLimit)1800         VibrationRecords(int sizeLimit, int aggregationTimeLimit) {
1801             super(sizeLimit, aggregationTimeLimit);
1802         }
1803 
1804         @Override
dumpGroupHeader(IndentingPrintWriter pw, int usage)1805         void dumpGroupHeader(IndentingPrintWriter pw, int usage) {
1806             pw.println(VibrationAttributes.usageToString(usage) + ":");
1807         }
1808 
1809         @Override
findGroupKeyProtoFieldId(int usage)1810         long findGroupKeyProtoFieldId(int usage) {
1811             return switch (usage) {
1812                 case VibrationAttributes.USAGE_RINGTONE ->
1813                     VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS;
1814                 case VibrationAttributes.USAGE_NOTIFICATION ->
1815                     VibratorManagerServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS;
1816                 case VibrationAttributes.USAGE_ALARM ->
1817                     VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS;
1818                 default ->
1819                     VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS;
1820             };
1821         }
1822     }
1823 
1824     /**
1825      * Record for a single {@link Vibration.DebugInfo}, that can be grouped by usage and aggregated
1826      * by UID, {@link VibrationAttributes} and {@link VibrationEffect}.
1827      */
1828     private static final class VibrationRecord
1829             implements GroupedAggregatedLogRecords.SingleLogRecord {
1830         private final Vibration.DebugInfo mInfo;
1831 
VibrationRecord(Vibration.DebugInfo info)1832         VibrationRecord(Vibration.DebugInfo info) {
1833             mInfo = info;
1834         }
1835 
1836         @Override
getGroupKey()1837         public int getGroupKey() {
1838             return mInfo.mCallerInfo.attrs.getUsage();
1839         }
1840 
1841         @Override
getCreateUptimeMs()1842         public long getCreateUptimeMs() {
1843             return mInfo.mCreateTime;
1844         }
1845 
1846         @Override
mayAggregate(GroupedAggregatedLogRecords.SingleLogRecord record)1847         public boolean mayAggregate(GroupedAggregatedLogRecords.SingleLogRecord record) {
1848             if (!(record instanceof VibrationRecord)) {
1849                 return false;
1850             }
1851             Vibration.DebugInfo info = ((VibrationRecord) record).mInfo;
1852             return mInfo.mCallerInfo.uid == info.mCallerInfo.uid
1853                     && Objects.equals(mInfo.mCallerInfo.attrs, info.mCallerInfo.attrs)
1854                     && Objects.equals(mInfo.mPlayedEffect, info.mPlayedEffect);
1855         }
1856 
1857         @Override
dump(IndentingPrintWriter pw)1858         public void dump(IndentingPrintWriter pw) {
1859             // Prints a compact version of each vibration request for dumpsys.
1860             mInfo.dumpCompact(pw);
1861         }
1862 
1863         @Override
dump(ProtoOutputStream proto, long fieldId)1864         public void dump(ProtoOutputStream proto, long fieldId) {
1865             mInfo.dump(proto, fieldId);
1866         }
1867     }
1868 
1869     /** Clears mNextVibration if set, ending it cleanly */
1870     @GuardedBy("mLock")
clearNextVibrationLocked(Vibration.EndInfo vibrationEndInfo)1871     private void clearNextVibrationLocked(Vibration.EndInfo vibrationEndInfo) {
1872         if (mNextVibration != null) {
1873             if (DEBUG) {
1874                 Slog.d(TAG, "Dropping pending vibration " + mNextVibration.getVibration().id
1875                         + " with end info: " + vibrationEndInfo);
1876             }
1877             // Clearing next vibration before playing it, end it and report metrics right away.
1878             endVibrationLocked(mNextVibration.getVibration(), vibrationEndInfo,
1879                     /* shouldWriteStats= */ true);
1880             mNextVibration = null;
1881         }
1882     }
1883 
1884     /**
1885      * Ends the external vibration, and clears related service state.
1886      *
1887      * @param vibrationEndInfo        the status and related info to end the associated Vibration
1888      * @param continueExternalControl indicates whether external control will continue. If not, the
1889      *                                HAL will have external control turned off.
1890      */
1891     @GuardedBy("mLock")
endExternalVibrateLocked(Vibration.EndInfo vibrationEndInfo, boolean continueExternalControl)1892     private void endExternalVibrateLocked(Vibration.EndInfo vibrationEndInfo,
1893             boolean continueExternalControl) {
1894         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "endExternalVibrateLocked");
1895         try {
1896             if (mCurrentExternalVibration == null) {
1897                 return;
1898             }
1899             mCurrentExternalVibration.unlinkToDeath();
1900             if (!continueExternalControl) {
1901                 setExternalControl(false, mCurrentExternalVibration.stats);
1902             }
1903             // The external control was turned off, end it and report metrics right away.
1904             endVibrationAndWriteStatsLocked(mCurrentExternalVibration, vibrationEndInfo);
1905             mCurrentExternalVibration = null;
1906         } finally {
1907             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1908         }
1909     }
1910 
getHapticVibrationProvider()1911     private HapticFeedbackVibrationProvider getHapticVibrationProvider() {
1912         synchronized (mLock) {
1913             // Used a cached haptic vibration provider if one exists.
1914             if (mHapticFeedbackVibrationProvider != null) {
1915                 return mHapticFeedbackVibrationProvider;
1916             }
1917             VibratorInfo combinedVibratorInfo = getCombinedVibratorInfo();
1918             if (combinedVibratorInfo == null) {
1919                 return null;
1920             }
1921             return mHapticFeedbackVibrationProvider =
1922                     mInjector.createHapticFeedbackVibrationProvider(
1923                             mContext.getResources(), combinedVibratorInfo);
1924         }
1925     }
1926 
getCombinedVibratorInfo()1927     private VibratorInfo getCombinedVibratorInfo() {
1928         synchronized (mLock) {
1929             // Used a cached resolving vibrator if one exists.
1930             if (mCombinedVibratorInfo != null) {
1931                 return mCombinedVibratorInfo;
1932             }
1933 
1934             // Return an empty resolving vibrator if the service has no vibrator.
1935             if (mVibratorIds.length == 0) {
1936                 return mCombinedVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO;
1937             }
1938 
1939             // Combine the vibrator infos of all the service's vibrator to create a single resolving
1940             // vibrator that is based on the combined info.
1941             VibratorInfo[] infos = new VibratorInfo[mVibratorIds.length];
1942             for (int i = 0; i < mVibratorIds.length; i++) {
1943                 VibratorInfo info = getVibratorInfo(mVibratorIds[i]);
1944                 // If any one of the service's vibrator does not have a valid vibrator info, stop
1945                 // trying to create and cache a combined resolving vibrator. Combine the infos only
1946                 // when infos for all vibrators are available.
1947                 if (info == null) {
1948                     return null;
1949                 }
1950                 infos[i] = info;
1951             }
1952 
1953             return mCombinedVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, infos);
1954         }
1955     }
1956 
1957     /** Implementation of {@link IExternalVibratorService} to be triggered on external control. */
1958     @VisibleForTesting
1959     final class ExternalVibratorService extends IExternalVibratorService.Stub {
1960 
1961         @Override
onExternalVibrationStart(ExternalVibration vib)1962         public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) {
1963             // Create Vibration.Stats as close to the received request as possible, for tracking.
1964             ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
1965             // Mute the request until we run all the checks and accept the vibration.
1966             vibHolder.scale.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
1967             boolean alreadyUnderExternalControl = false;
1968             boolean waitForCompletion = false;
1969 
1970             synchronized (mLock) {
1971                 if (!hasExternalControlCapability()) {
1972                     endVibrationAndWriteStatsLocked(vibHolder,
1973                             new Vibration.EndInfo(Vibration.Status.IGNORED_UNSUPPORTED));
1974                     return vibHolder.scale;
1975                 }
1976 
1977                 if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
1978                         vib.getUid(), -1 /*owningUid*/, true /*exported*/)
1979                         != PackageManager.PERMISSION_GRANTED) {
1980                     Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
1981                             + " tried to play externally controlled vibration"
1982                             + " without VIBRATE permission, ignoring.");
1983                     endVibrationAndWriteStatsLocked(vibHolder,
1984                             new Vibration.EndInfo(Vibration.Status.IGNORED_MISSING_PERMISSION));
1985                     return vibHolder.scale;
1986                 }
1987 
1988                 Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked(
1989                         vibHolder.callerInfo);
1990 
1991                 if (vibrationEndInfo == null
1992                         && mCurrentExternalVibration != null
1993                         && mCurrentExternalVibration.isHoldingSameVibration(vib)) {
1994                     // We are already playing this external vibration, so we can return the same
1995                     // scale calculated in the previous call to this method.
1996                     return mCurrentExternalVibration.scale;
1997                 }
1998 
1999                 if (vibrationEndInfo == null) {
2000                     // Check if ongoing vibration is more important than this vibration.
2001                     vibrationEndInfo = shouldIgnoreVibrationForOngoingLocked(vibHolder);
2002                 }
2003 
2004                 if (vibrationEndInfo != null) {
2005                     endVibrationAndWriteStatsLocked(vibHolder, vibrationEndInfo);
2006                     return vibHolder.scale;
2007                 }
2008 
2009                 if (mCurrentExternalVibration == null) {
2010                     // If we're not under external control right now, then cancel any normal
2011                     // vibration that may be playing and ready the vibrator for external control.
2012                     if (mCurrentVibration != null) {
2013                         vibHolder.stats.reportInterruptedAnotherVibration(
2014                                 mCurrentVibration.getVibration().callerInfo);
2015                         clearNextVibrationLocked(
2016                                 new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_EXTERNAL,
2017                                         vibHolder.callerInfo));
2018                         mCurrentVibration.notifyCancelled(
2019                                 new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
2020                                         vibHolder.callerInfo),
2021                                 /* immediate= */ true);
2022                         waitForCompletion = true;
2023                     }
2024                 } else {
2025                     // At this point we have an externally controlled vibration playing already.
2026                     // Since the interface defines that only one externally controlled vibration can
2027                     // play at a time, we need to first mute the ongoing vibration and then return
2028                     // a scale from this function for the new one, so we can be assured that the
2029                     // ongoing will be muted in favor of the new vibration.
2030                     //
2031                     // Note that this doesn't support multiple concurrent external controls, as we
2032                     // would need to mute the old one still if it came from a different controller.
2033                     alreadyUnderExternalControl = true;
2034                     mCurrentExternalVibration.mute();
2035                     vibHolder.stats.reportInterruptedAnotherVibration(
2036                             mCurrentExternalVibration.callerInfo);
2037                     endExternalVibrateLocked(
2038                             new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
2039                                     vibHolder.callerInfo),
2040                             /* continueExternalControl= */ true);
2041                 }
2042 
2043                 VibrationAttributes attrs = fixupVibrationAttributes(vib.getVibrationAttributes(),
2044                         /* effect= */ null);
2045                 if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
2046                     // Force update of user settings before checking if this vibration effect should
2047                     // be ignored or scaled.
2048                     mVibrationSettings.update();
2049                 }
2050 
2051                 mCurrentExternalVibration = vibHolder;
2052                 vibHolder.linkToDeath();
2053                 vibHolder.scale(mVibrationScaler, attrs.getUsage());
2054             }
2055 
2056             if (waitForCompletion) {
2057                 if (!mVibrationThread.waitForThreadIdle(VIBRATION_CANCEL_WAIT_MILLIS)) {
2058                     Slog.e(TAG, "Timed out waiting for vibration to cancel");
2059                     synchronized (mLock) {
2060                         // Trigger endExternalVibrateLocked to unlink to death recipient.
2061                         endExternalVibrateLocked(
2062                                 new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_CANCELLING),
2063                                 /* continueExternalControl= */ false);
2064                         // Mute the request, vibration will be ignored.
2065                         vibHolder.scale.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
2066                     }
2067                     return vibHolder.scale;
2068                 }
2069             }
2070             if (!alreadyUnderExternalControl) {
2071                 if (DEBUG) {
2072                     Slog.d(TAG, "Vibrator going under external control.");
2073                 }
2074                 setExternalControl(true, vibHolder.stats);
2075             }
2076             if (DEBUG) {
2077                 Slog.d(TAG, "Playing external vibration: " + vib);
2078             }
2079             // Vibrator will start receiving data from external channels after this point.
2080             // Report current time as the vibration start time, for debugging.
2081             vibHolder.stats.reportStarted();
2082             return vibHolder.scale;
2083         }
2084 
2085         @Override
onExternalVibrationStop(ExternalVibration vib)2086         public void onExternalVibrationStop(ExternalVibration vib) {
2087             synchronized (mLock) {
2088                 if (mCurrentExternalVibration != null
2089                         && mCurrentExternalVibration.isHoldingSameVibration(vib)) {
2090                     if (DEBUG) {
2091                         Slog.d(TAG, "Stopping external vibration: " + vib);
2092                     }
2093                     endExternalVibrateLocked(
2094                             new Vibration.EndInfo(Vibration.Status.FINISHED),
2095                             /* continueExternalControl= */ false);
2096                 }
2097             }
2098         }
2099 
hasExternalControlCapability()2100         private boolean hasExternalControlCapability() {
2101             for (int i = 0; i < mVibrators.size(); i++) {
2102                 if (mVibrators.valueAt(i).hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
2103                     return true;
2104                 }
2105             }
2106             return false;
2107         }
2108     }
2109 
2110     /** Provide limited functionality from {@link VibratorManagerService} as shell commands. */
2111     private final class VibratorManagerShellCommand extends ShellCommand {
2112         public static final String SHELL_PACKAGE_NAME = "com.android.shell";
2113         public static final long VIBRATION_END_TIMEOUT_MS = 500; // Clean up shouldn't be too long.
2114 
2115         private final class CommonOptions {
2116             public boolean force = false;
2117             public String description = "Shell command";
2118             public boolean background = false;
2119 
CommonOptions()2120             CommonOptions() {
2121                 String nextArg;
2122                 while ((nextArg = peekNextArg()) != null) {
2123                     switch (nextArg) {
2124                         case "-f":
2125                             getNextArgRequired(); // consume "-f"
2126                             force = true;
2127                             break;
2128                         case "-B":
2129                             getNextArgRequired(); // consume "-B"
2130                             background = true;
2131                             break;
2132                         case "-d":
2133                             getNextArgRequired(); // consume "-d"
2134                             description = getNextArgRequired();
2135                             break;
2136                         default:
2137                             // nextArg is not a common option, finish reading.
2138                             return;
2139                     }
2140                 }
2141             }
2142         }
2143 
2144         private final IBinder mShellCallbacksToken;
2145 
VibratorManagerShellCommand(IBinder shellCallbacksToken)2146         private VibratorManagerShellCommand(IBinder shellCallbacksToken) {
2147             mShellCallbacksToken = shellCallbacksToken;
2148         }
2149 
2150         @Override
onCommand(String cmd)2151         public int onCommand(String cmd) {
2152             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "onCommand " + cmd);
2153             try {
2154                 if ("list".equals(cmd)) {
2155                     return runListVibrators();
2156                 }
2157                 if ("synced".equals(cmd)) {
2158                     return runMono();
2159                 }
2160                 if ("combined".equals(cmd)) {
2161                     return runStereo();
2162                 }
2163                 if ("sequential".equals(cmd)) {
2164                     return runSequential();
2165                 }
2166                 if ("xml".equals(cmd)) {
2167                     return runXml();
2168                 }
2169                 if ("cancel".equals(cmd)) {
2170                     return runCancel();
2171                 }
2172                 if ("feedback".equals(cmd)) {
2173                     return runHapticFeedback();
2174                 }
2175                 return handleDefaultCommands(cmd);
2176             } finally {
2177                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
2178             }
2179         }
2180 
runListVibrators()2181         private int runListVibrators() {
2182             try (PrintWriter pw = getOutPrintWriter();) {
2183                 if (mVibratorIds.length == 0) {
2184                     pw.println("No vibrator found");
2185                 } else {
2186                     for (int id : mVibratorIds) {
2187                         pw.println(id);
2188                     }
2189                 }
2190                 pw.println("");
2191                 return 0;
2192             }
2193         }
2194 
2195         /**
2196          * Runs a CombinedVibration using the configured common options and attributes.
2197          */
runVibrate(CommonOptions commonOptions, CombinedVibration combined)2198         private void runVibrate(CommonOptions commonOptions, CombinedVibration combined) {
2199             VibrationAttributes attrs = createVibrationAttributes(commonOptions);
2200             // If running in the background, bind to death of the server binder rather than the
2201             // client, and the cancel command likewise uses the server binder reference to
2202             // only cancel background vibrations.
2203             IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
2204                     : mShellCallbacksToken;
2205             int uid = Binder.getCallingUid();
2206             // Resolve the package name for the client based on the process UID, to cover cases like
2207             // rooted shell clients using ROOT_UID.
2208             String resolvedPackageName = AppOpsManager.resolvePackageName(uid, SHELL_PACKAGE_NAME);
2209             HalVibration vib = vibrateWithPermissionCheck(uid, Context.DEVICE_ID_DEFAULT,
2210                     resolvedPackageName, combined, attrs, commonOptions.description, deathBinder);
2211             maybeWaitOnVibration(vib, commonOptions);
2212         }
2213 
runMono()2214         private int runMono() {
2215             runVibrate(new CommonOptions(), CombinedVibration.createParallel(nextEffect()));
2216             return 0;
2217         }
2218 
runStereo()2219         private int runStereo() {
2220             CommonOptions commonOptions = new CommonOptions();
2221             CombinedVibration.ParallelCombination combination =
2222                     CombinedVibration.startParallel();
2223             while ("-v".equals(getNextOption())) {
2224                 int vibratorId = Integer.parseInt(getNextArgRequired());
2225                 combination.addVibrator(vibratorId, nextEffect());
2226             }
2227             runVibrate(commonOptions, combination.combine());
2228             return 0;
2229         }
2230 
runSequential()2231         private int runSequential() {
2232             CommonOptions commonOptions = new CommonOptions();
2233             CombinedVibration.SequentialCombination combination =
2234                     CombinedVibration.startSequential();
2235             while ("-v".equals(getNextOption())) {
2236                 int vibratorId = Integer.parseInt(getNextArgRequired());
2237                 combination.addNext(vibratorId, nextEffect());
2238             }
2239             runVibrate(commonOptions, combination.combine());
2240             return 0;
2241         }
2242 
runXml()2243         private int runXml() {
2244             CommonOptions commonOptions = new CommonOptions();
2245             String xml = getNextArgRequired();
2246             CombinedVibration vibration = parseXml(xml);
2247             runVibrate(commonOptions, vibration);
2248             return 0;
2249         }
2250 
runCancel()2251         private int runCancel() {
2252             // Cancel is only needed if the vibration was run in the background, otherwise it's
2253             // terminated by the shell command ending. In these cases, the token was that of the
2254             // service rather than the client.
2255             cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, VibratorManagerService.this);
2256             return 0;
2257         }
2258 
runHapticFeedback()2259         private int runHapticFeedback() {
2260             CommonOptions commonOptions = new CommonOptions();
2261             int constant = Integer.parseInt(getNextArgRequired());
2262 
2263             IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
2264                     : mShellCallbacksToken;
2265             HalVibration vib = performHapticFeedbackInternal(Binder.getCallingUid(),
2266                     Context.DEVICE_ID_DEFAULT, SHELL_PACKAGE_NAME, constant,
2267                     /* always= */ commonOptions.force, /* reason= */ commonOptions.description,
2268                     deathBinder, false /* fromIme */);
2269             maybeWaitOnVibration(vib, commonOptions);
2270 
2271             return 0;
2272         }
2273 
nextEffect()2274         private VibrationEffect nextEffect() {
2275             VibrationEffect.Composition composition = VibrationEffect.startComposition();
2276             String nextArg;
2277 
2278             while ((nextArg = peekNextArg()) != null) {
2279                 if ("oneshot".equals(nextArg)) {
2280                     addOneShotToComposition(composition);
2281                 } else if ("waveform".equals(nextArg)) {
2282                     addWaveformToComposition(composition);
2283                 } else if ("prebaked".equals(nextArg)) {
2284                     addPrebakedToComposition(composition);
2285                 } else if ("primitives".equals(nextArg)) {
2286                     addPrimitivesToComposition(composition);
2287                 } else {
2288                     // nextArg is not an effect, finish reading.
2289                     break;
2290                 }
2291             }
2292 
2293             return composition.compose();
2294         }
2295 
addOneShotToComposition(VibrationEffect.Composition composition)2296         private void addOneShotToComposition(VibrationEffect.Composition composition) {
2297             boolean hasAmplitude = false;
2298             int delay = 0;
2299 
2300             getNextArgRequired(); // consume "oneshot"
2301             String nextOption;
2302             while ((nextOption = getNextOption()) != null) {
2303                 if ("-a".equals(nextOption)) {
2304                     hasAmplitude = true;
2305                 } else if ("-w".equals(nextOption)) {
2306                     delay = Integer.parseInt(getNextArgRequired());
2307                 }
2308             }
2309 
2310             long duration = Long.parseLong(getNextArgRequired());
2311             int amplitude = hasAmplitude ? Integer.parseInt(getNextArgRequired())
2312                     : VibrationEffect.DEFAULT_AMPLITUDE;
2313             composition.addOffDuration(Duration.ofMillis(delay));
2314             composition.addEffect(VibrationEffect.createOneShot(duration, amplitude));
2315         }
2316 
addWaveformToComposition(VibrationEffect.Composition composition)2317         private void addWaveformToComposition(VibrationEffect.Composition composition) {
2318             boolean hasAmplitudes = false;
2319             boolean hasFrequencies = false;
2320             boolean isContinuous = false;
2321             int repeat = -1;
2322             int delay = 0;
2323 
2324             getNextArgRequired(); // consume "waveform"
2325             String nextOption;
2326             while ((nextOption = getNextOption()) != null) {
2327                 if ("-a".equals(nextOption)) {
2328                     hasAmplitudes = true;
2329                 } else if ("-r".equals(nextOption)) {
2330                     repeat = Integer.parseInt(getNextArgRequired());
2331                 } else if ("-w".equals(nextOption)) {
2332                     delay = Integer.parseInt(getNextArgRequired());
2333                 } else if ("-f".equals(nextOption)) {
2334                     hasFrequencies = true;
2335                 } else if ("-c".equals(nextOption)) {
2336                     isContinuous = true;
2337                 }
2338             }
2339             List<Integer> durations = new ArrayList<>();
2340             List<Float> amplitudes = new ArrayList<>();
2341             List<Float> frequencies = new ArrayList<>();
2342 
2343             float nextAmplitude = 0;
2344             String nextArg;
2345             while ((nextArg = peekNextArg()) != null) {
2346                 try {
2347                     durations.add(Integer.parseInt(nextArg));
2348                     getNextArgRequired(); // consume the duration
2349                 } catch (NumberFormatException e) {
2350                     // nextArg is not a duration, finish reading.
2351                     break;
2352                 }
2353                 if (hasAmplitudes) {
2354                     amplitudes.add(
2355                             Float.parseFloat(getNextArgRequired()) / VibrationEffect.MAX_AMPLITUDE);
2356                 } else {
2357                     amplitudes.add(nextAmplitude);
2358                     nextAmplitude = 1 - nextAmplitude;
2359                 }
2360                 if (hasFrequencies) {
2361                     frequencies.add(Float.parseFloat(getNextArgRequired()));
2362                 }
2363             }
2364 
2365             // Add delay before the waveform.
2366             composition.addOffDuration(Duration.ofMillis(delay));
2367 
2368             VibrationEffect.WaveformBuilder waveform = VibrationEffect.startWaveform();
2369             for (int i = 0; i < durations.size(); i++) {
2370                 Duration transitionDuration = isContinuous
2371                         ? Duration.ofMillis(durations.get(i))
2372                         : Duration.ZERO;
2373                 Duration sustainDuration = isContinuous
2374                         ? Duration.ZERO
2375                         : Duration.ofMillis(durations.get(i));
2376 
2377                 if (hasFrequencies) {
2378                     waveform.addTransition(transitionDuration, targetAmplitude(amplitudes.get(i)),
2379                             targetFrequency(frequencies.get(i)));
2380                 } else {
2381                     waveform.addTransition(transitionDuration, targetAmplitude(amplitudes.get(i)));
2382                 }
2383                 if (!sustainDuration.isZero()) {
2384                     // Add sustain only takes positive durations. Skip this since we already
2385                     // did a transition to the desired values (even when duration is zero).
2386                     waveform.addSustain(sustainDuration);
2387                 }
2388 
2389                 if ((i > 0) && (i == repeat)) {
2390                     // Add segment that is not repeated to the composition and reset builder.
2391                     composition.addEffect(waveform.build());
2392 
2393                     if (hasFrequencies) {
2394                         waveform = VibrationEffect.startWaveform(targetAmplitude(amplitudes.get(i)),
2395                                 targetFrequency(frequencies.get(i)));
2396                     } else {
2397                         waveform = VibrationEffect.startWaveform(
2398                                 targetAmplitude(amplitudes.get(i)));
2399                     }
2400                 }
2401             }
2402             if (repeat < 0) {
2403                 composition.addEffect(waveform.build());
2404             } else {
2405                 // The waveform was already split at the repeat index, just repeat what remains.
2406                 composition.repeatEffectIndefinitely(waveform.build());
2407             }
2408         }
2409 
addPrebakedToComposition(VibrationEffect.Composition composition)2410         private void addPrebakedToComposition(VibrationEffect.Composition composition) {
2411             boolean shouldFallback = false;
2412             int delay = 0;
2413 
2414             getNextArgRequired(); // consume "prebaked"
2415             String nextOption;
2416             while ((nextOption = getNextOption()) != null) {
2417                 if ("-b".equals(nextOption)) {
2418                     shouldFallback = true;
2419                 } else if ("-w".equals(nextOption)) {
2420                     delay = Integer.parseInt(getNextArgRequired());
2421                 }
2422             }
2423 
2424             int effectId = Integer.parseInt(getNextArgRequired());
2425             composition.addOffDuration(Duration.ofMillis(delay));
2426             composition.addEffect(VibrationEffect.get(effectId, shouldFallback));
2427         }
2428 
addPrimitivesToComposition(VibrationEffect.Composition composition)2429         private void addPrimitivesToComposition(VibrationEffect.Composition composition) {
2430             getNextArgRequired(); // consume "primitives"
2431             String nextArg;
2432             while ((nextArg = peekNextArg()) != null) {
2433                 int delay = 0;
2434                 if ("-w".equals(nextArg)) {
2435                     getNextArgRequired(); // consume "-w"
2436                     delay = Integer.parseInt(getNextArgRequired());
2437                     nextArg = peekNextArg();
2438                 }
2439                 try {
2440                     composition.addPrimitive(Integer.parseInt(nextArg), /* scale= */ 1, delay);
2441                     getNextArgRequired(); // consume the primitive id
2442                 } catch (NumberFormatException | NullPointerException e) {
2443                     // nextArg is not describing a primitive, leave it to be consumed by outer loops
2444                     break;
2445                 }
2446             }
2447         }
2448 
createVibrationAttributes(CommonOptions commonOptions)2449         private VibrationAttributes createVibrationAttributes(CommonOptions commonOptions) {
2450             // This will bypass user settings, Do Not Disturb and other interruption policies.
2451             final int flags = commonOptions.force ? ATTRIBUTES_ALL_BYPASS_FLAGS : 0;
2452             return new VibrationAttributes.Builder()
2453                     .setFlags(flags)
2454                     // Used to allow vibrations when the adb shell process is running in background.
2455                     // This will apply the NOTIFICATION_VIBRATION_INTENSITY setting.
2456                     .setUsage(VibrationAttributes.USAGE_COMMUNICATION_REQUEST)
2457                     .build();
2458         }
2459 
parseXml(String xml)2460         private CombinedVibration parseXml(String xml) {
2461             try {
2462                 ParsedVibration parsedVibration =
2463                         VibrationXmlParser.parseDocument(new StringReader(xml));
2464                 if (parsedVibration == null) {
2465                     throw new IllegalArgumentException("Error parsing vibration XML " + xml);
2466                 }
2467                 VibratorInfo combinedVibratorInfo = getCombinedVibratorInfo();
2468                 if (combinedVibratorInfo == null) {
2469                     throw new IllegalStateException(
2470                             "No combined vibrator info to parse vibration XML " + xml);
2471                 }
2472                 VibrationEffect effect = parsedVibration.resolve(combinedVibratorInfo);
2473                 if (effect == null) {
2474                     throw new IllegalArgumentException(
2475                             "Parsed vibration cannot be resolved for vibration XML " + xml);
2476                 }
2477                 return CombinedVibration.createParallel(effect);
2478             } catch (IOException e) {
2479                 throw new RuntimeException("Error parsing vibration XML " + xml, e);
2480             }
2481         }
2482 
maybeWaitOnVibration(HalVibration vib, CommonOptions commonOptions)2483         private void maybeWaitOnVibration(HalVibration vib, CommonOptions commonOptions) {
2484             if (vib != null && !commonOptions.background) {
2485                 try {
2486                     // Waits for the client vibration to finish, but the VibrationThread may still
2487                     // do cleanup after this.
2488                     vib.waitForEnd();
2489                     // Wait for vibration clean up and possible ramp down before ending.
2490                     mVibrationThread.waitForThreadIdle(
2491                             mVibrationSettings.getRampDownDuration() + VIBRATION_END_TIMEOUT_MS);
2492                 } catch (InterruptedException e) {
2493                 }
2494             }
2495         }
2496 
2497         @Override
onHelp()2498         public void onHelp() {
2499             try (PrintWriter pw = getOutPrintWriter();) {
2500                 pw.println("Vibrator Manager commands:");
2501                 pw.println("  help");
2502                 pw.println("    Prints this help text.");
2503                 pw.println("");
2504                 pw.println("  list");
2505                 pw.println("    Prints the id of device vibrators. This does not include any ");
2506                 pw.println("    connected input device.");
2507                 pw.println("  synced [options] <effect>...");
2508                 pw.println("    Vibrates effect on all vibrators in sync.");
2509                 pw.println("  combined [options] (-v <vibrator-id> <effect>...)...");
2510                 pw.println("    Vibrates different effects on each vibrator in sync.");
2511                 pw.println("  sequential [options] (-v <vibrator-id> <effect>...)...");
2512                 pw.println("    Vibrates different effects on each vibrator in sequence.");
2513                 pw.println("  xml [options] <xml>");
2514                 pw.println("    Vibrates using combined vibration described in given XML string");
2515                 pw.println("    on all vibrators in sync. The XML could be:");
2516                 pw.println("        XML containing a single effect, or");
2517                 pw.println("        A vibration select XML containing multiple effects.");
2518                 pw.println("    Vibrates using combined vibration described in given XML string.");
2519                 pw.println("    XML containing a single effect it runs on all vibrators in sync.");
2520                 pw.println("  cancel");
2521                 pw.println("    Cancels any active vibration");
2522                 pw.println("  feedback [-f] [-d <description>] <constant>");
2523                 pw.println("    Performs a haptic feedback with the given constant.");
2524                 pw.println("    The force (-f) option enables the `always` configuration, which");
2525                 pw.println("    plays the haptic irrespective of the vibration intensity settings");
2526                 pw.println("");
2527                 pw.println("Effect commands:");
2528                 pw.println("  oneshot [-w delay] [-a] <duration> [<amplitude>]");
2529                 pw.println("    Vibrates for duration milliseconds; ignored when device is on ");
2530                 pw.println("    DND (Do Not Disturb) mode; touch feedback strength user setting ");
2531                 pw.println("    will be used to scale amplitude.");
2532                 pw.println("    If -w is provided, the effect will be played after the specified");
2533                 pw.println("    wait time in milliseconds.");
2534                 pw.println("    If -a is provided, the command accepts a second argument for ");
2535                 pw.println("    amplitude, in a scale of 1-255.");
2536                 pw.print("  waveform [-w delay] [-r index] [-a] [-f] [-c] ");
2537                 pw.println("(<duration> [<amplitude>] [<frequency>])...");
2538                 pw.println("    Vibrates for durations and amplitudes in list; ignored when ");
2539                 pw.println("    device is on DND (Do Not Disturb) mode; touch feedback strength ");
2540                 pw.println("    user setting will be used to scale amplitude.");
2541                 pw.println("    If -w is provided, the effect will be played after the specified");
2542                 pw.println("    wait time in milliseconds.");
2543                 pw.println("    If -r is provided, the waveform loops back to the specified");
2544                 pw.println("    index (e.g. 0 loops from the beginning)");
2545                 pw.println("    If -a is provided, the command expects amplitude to follow each");
2546                 pw.println("    duration; otherwise, it accepts durations only and alternates");
2547                 pw.println("    off/on");
2548                 pw.println("    If -f is provided, the command expects frequency to follow each");
2549                 pw.println("    amplitude or duration; otherwise, it uses resonant frequency");
2550                 pw.println("    If -c is provided, the waveform is continuous and will ramp");
2551                 pw.println("    between values; otherwise each entry is a fixed step.");
2552                 pw.println("    Duration is in milliseconds; amplitude is a scale of 1-255;");
2553                 pw.println("    frequency is an absolute value in hertz;");
2554                 pw.println("  prebaked [-w delay] [-b] <effect-id>");
2555                 pw.println("    Vibrates with prebaked effect; ignored when device is on DND ");
2556                 pw.println("    (Do Not Disturb) mode; touch feedback strength user setting ");
2557                 pw.println("    will be used to scale amplitude.");
2558                 pw.println("    If -w is provided, the effect will be played after the specified");
2559                 pw.println("    wait time in milliseconds.");
2560                 pw.println("    If -b is provided, the prebaked fallback effect will be played if");
2561                 pw.println("    the device doesn't support the given effect-id.");
2562                 pw.println("  primitives ([-w delay] <primitive-id>)...");
2563                 pw.println("    Vibrates with a composed effect; ignored when device is on DND ");
2564                 pw.println("    (Do Not Disturb) mode; touch feedback strength user setting ");
2565                 pw.println("    will be used to scale primitive intensities.");
2566                 pw.println("    If -w is provided, the next primitive will be played after the ");
2567                 pw.println("    specified wait time in milliseconds.");
2568                 pw.println("");
2569                 pw.println("Common Options:");
2570                 pw.println("  -f");
2571                 pw.println("    Force. Ignore Do Not Disturb setting.");
2572                 pw.println("  -B");
2573                 pw.println("    Run in the background; without this option the shell cmd will");
2574                 pw.println("    block until the vibration has completed.");
2575                 pw.println("  -d <description>");
2576                 pw.println("    Add description to the vibration.");
2577                 pw.println("");
2578             }
2579         }
2580     }
2581 }
2582