• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.Trace.TRACE_TAG_VIBRATOR;
20 
21 import android.annotation.Nullable;
22 import android.hardware.vibrator.IVibrator;
23 import android.os.Binder;
24 import android.os.IVibratorStateListener;
25 import android.os.Parcel;
26 import android.os.RemoteCallbackList;
27 import android.os.RemoteException;
28 import android.os.Trace;
29 import android.os.VibrationEffect;
30 import android.os.VibratorInfo;
31 import android.os.vibrator.PrebakedSegment;
32 import android.os.vibrator.PrimitiveSegment;
33 import android.os.vibrator.PwlePoint;
34 import android.os.vibrator.RampSegment;
35 import android.util.IndentingPrintWriter;
36 import android.util.Slog;
37 
38 import com.android.internal.annotations.GuardedBy;
39 import com.android.internal.annotations.VisibleForTesting;
40 
41 import libcore.util.NativeAllocationRegistry;
42 
43 /** Controls a single vibrator. */
44 final class VibratorController {
45     private static final String TAG = "VibratorController";
46 
47     private final Object mLock = new Object();
48 
49     @GuardedBy("mLock")
50     private final NativeWrapper mNativeWrapper;
51 
52     // Vibrator state listeners that support concurrent updates and broadcasts, but should lock
53     // while broadcasting to guarantee delivery order.
54     private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
55             new RemoteCallbackList<>();
56 
57     // Vibrator state variables that are updated from synchronized blocks but can be read anytime
58     // for a snippet of the current known vibrator state/info.
59     private volatile VibratorInfo mVibratorInfo;
60     private volatile boolean mVibratorInfoLoadSuccessful;
61     private volatile VibratorState mCurrentState;
62     private volatile float mCurrentAmplitude;
63 
64     /**
65      * Listener for vibration completion callbacks from native.
66      *
67      * <p>Only the latest active native call to {@link VibratorController#on} will ever trigger this
68      * completion callback, to avoid race conditions during a vibration playback. If a new call to
69      * {@link #on} or {@link #off} happens before a previous callback was triggered then the
70      * previous callback will be disabled, even if the new command fails.
71      */
72     public interface OnVibrationCompleteListener {
73 
74         /** Callback triggered when an active vibration command is complete. */
onComplete(int vibratorId, long vibrationId, long stepId)75         void onComplete(int vibratorId, long vibrationId, long stepId);
76     }
77 
78     /** Representation of the vibrator state based on the interactions through this controller. */
79     private enum VibratorState {
80         IDLE, VIBRATING, UNDER_EXTERNAL_CONTROL
81     }
82 
VibratorController(int vibratorId, OnVibrationCompleteListener listener)83     VibratorController(int vibratorId, OnVibrationCompleteListener listener) {
84         this(vibratorId, listener, new NativeWrapper());
85     }
86 
87     @VisibleForTesting
VibratorController(int vibratorId, OnVibrationCompleteListener listener, NativeWrapper nativeWrapper)88     VibratorController(int vibratorId, OnVibrationCompleteListener listener,
89             NativeWrapper nativeWrapper) {
90         mNativeWrapper = nativeWrapper;
91         mNativeWrapper.init(vibratorId, listener);
92         VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
93         mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(vibratorInfoBuilder);
94         mVibratorInfo = vibratorInfoBuilder.build();
95         mCurrentState = VibratorState.IDLE;
96 
97         if (!mVibratorInfoLoadSuccessful) {
98             Slog.e(TAG,
99                     "Vibrator controller initialization failed to load some HAL info for vibrator "
100                             + vibratorId);
101         }
102     }
103 
104     /** Register state listener for this vibrator. */
registerVibratorStateListener(IVibratorStateListener listener)105     public boolean registerVibratorStateListener(IVibratorStateListener listener) {
106         final long token = Binder.clearCallingIdentity();
107         try {
108             // Register the listener and send the first state atomically, to avoid potentially
109             // out of order broadcasts in between.
110             synchronized (mLock) {
111                 if (!mVibratorStateListeners.register(listener)) {
112                     return false;
113                 }
114                 // Notify its callback after new client registered.
115                 notifyStateListener(listener, isVibrating(mCurrentState));
116             }
117             return true;
118         } finally {
119             Binder.restoreCallingIdentity(token);
120         }
121     }
122 
123     /** Remove registered state listener for this vibrator. */
unregisterVibratorStateListener(IVibratorStateListener listener)124     public boolean unregisterVibratorStateListener(IVibratorStateListener listener) {
125         final long token = Binder.clearCallingIdentity();
126         try {
127             return mVibratorStateListeners.unregister(listener);
128         } finally {
129             Binder.restoreCallingIdentity(token);
130         }
131     }
132 
133     /** Reruns the query to the vibrator to load the {@link VibratorInfo}, if not yet successful. */
reloadVibratorInfoIfNeeded()134     public void reloadVibratorInfoIfNeeded() {
135         Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#reloadVibratorInfoIfNeeded");
136         try {
137             // Early check outside lock, for quick return.
138             if (mVibratorInfoLoadSuccessful) {
139                 return;
140             }
141             synchronized (mLock) {
142                 if (mVibratorInfoLoadSuccessful) {
143                     return;
144                 }
145                 int vibratorId = mVibratorInfo.getId();
146                 VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
147                 mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(vibratorInfoBuilder);
148                 mVibratorInfo = vibratorInfoBuilder.build();
149                 if (!mVibratorInfoLoadSuccessful) {
150                     Slog.e(TAG, "Failed retry of HAL getInfo for vibrator " + vibratorId);
151                 }
152             }
153         } finally {
154             Trace.traceEnd(TRACE_TAG_VIBRATOR);
155         }
156     }
157 
158     /** Checks if the {@link VibratorInfo} was loaded from the vibrator hardware successfully. */
isVibratorInfoLoadSuccessful()159     boolean isVibratorInfoLoadSuccessful() {
160         return mVibratorInfoLoadSuccessful;
161     }
162 
163     /** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */
getVibratorInfo()164     public VibratorInfo getVibratorInfo() {
165         return mVibratorInfo;
166     }
167 
168     /**
169      * Return {@code true} is this vibrator is currently vibrating, false otherwise.
170      *
171      * <p>This state is controlled by calls to {@link #on} and {@link #off} methods, and is
172      * automatically notified to any registered {@link IVibratorStateListener} on change.
173      */
isVibrating()174     public boolean isVibrating() {
175         return isVibrating(mCurrentState);
176     }
177 
178     /**
179      * Returns the current amplitude the device is vibrating.
180      *
181      * <p>This value is set to 1 by the method {@link #on(long, long)}, and can be updated via
182      * {@link #setAmplitude(float)} if called while the device is vibrating.
183      *
184      * <p>If the device is vibrating via any other {@link #on} method then the current amplitude is
185      * unknown and this will return -1.
186      *
187      * <p>If {@link #isVibrating()} is false then this will be zero.
188      */
getCurrentAmplitude()189     public float getCurrentAmplitude() {
190         return mCurrentAmplitude;
191     }
192 
193     /**
194      * Check against this vibrator capabilities.
195      *
196      * @param capability one of IVibrator.CAP_*
197      * @return true if this vibrator has this capability, false otherwise
198      */
hasCapability(long capability)199     public boolean hasCapability(long capability) {
200         return mVibratorInfo.hasCapability(capability);
201     }
202 
203     /** Return {@code true} if the underlying vibrator is currently available, false otherwise. */
isAvailable()204     public boolean isAvailable() {
205         Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#isAvailable");
206         try {
207             synchronized (mLock) {
208                 return mNativeWrapper.isAvailable();
209             }
210         } finally {
211             Trace.traceEnd(TRACE_TAG_VIBRATOR);
212         }
213     }
214 
215     /**
216      * Set the vibrator control to be external or not, based on given flag.
217      *
218      * <p>This will affect the state of {@link #isVibrating()}.
219      */
setExternalControl(boolean externalControl)220     public void setExternalControl(boolean externalControl) {
221         Trace.traceBegin(TRACE_TAG_VIBRATOR,
222                 externalControl ? "VibratorController#enableExternalControl"
223                 : "VibratorController#disableExternalControl");
224         try {
225             if (!mVibratorInfo.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
226                 return;
227             }
228             VibratorState newState =
229                     externalControl ? VibratorState.UNDER_EXTERNAL_CONTROL : VibratorState.IDLE;
230             synchronized (mLock) {
231                 mNativeWrapper.setExternalControl(externalControl);
232                 updateStateAndNotifyListenersLocked(newState);
233             }
234         } finally {
235             Trace.traceEnd(TRACE_TAG_VIBRATOR);
236         }
237     }
238 
239     /**
240      * Update the predefined vibration effect saved with given id. This will remove the saved effect
241      * if given {@code effect} is {@code null}.
242      */
updateAlwaysOn(int id, @Nullable PrebakedSegment prebaked)243     public void updateAlwaysOn(int id, @Nullable PrebakedSegment prebaked) {
244         Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#updateAlwaysOn");
245         try {
246             if (!mVibratorInfo.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
247                 return;
248             }
249             synchronized (mLock) {
250                 if (prebaked == null) {
251                     mNativeWrapper.alwaysOnDisable(id);
252                 } else {
253                     mNativeWrapper.alwaysOnEnable(id, prebaked.getEffectId(),
254                             prebaked.getEffectStrength());
255                 }
256             }
257         } finally {
258             Trace.traceEnd(TRACE_TAG_VIBRATOR);
259         }
260     }
261 
262     /** Set the vibration amplitude. This will NOT affect the state of {@link #isVibrating()}. */
setAmplitude(float amplitude)263     public void setAmplitude(float amplitude) {
264         Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#setAmplitude");
265         try {
266             synchronized (mLock) {
267                 if (mVibratorInfo.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
268                     mNativeWrapper.setAmplitude(amplitude);
269                 }
270                 if (mCurrentState == VibratorState.VIBRATING) {
271                     mCurrentAmplitude = amplitude;
272                 }
273             }
274         } finally {
275             Trace.traceEnd(TRACE_TAG_VIBRATOR);
276         }
277     }
278 
279     /**
280      * Turn on the vibrator for {@code milliseconds} time, using {@code vibrationId} for completion
281      * callback to {@link OnVibrationCompleteListener}.
282      *
283      * <p>This will affect the state of {@link #isVibrating()}.
284      *
285      * @return The positive duration of the vibration started, if successful, zero if the vibrator
286      * do not support the input or a negative number if the operation failed.
287      */
on(long milliseconds, long vibrationId, long stepId)288     public long on(long milliseconds, long vibrationId, long stepId) {
289         Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on");
290         try {
291             synchronized (mLock) {
292                 long duration = mNativeWrapper.on(milliseconds, vibrationId, stepId);
293                 if (duration > 0) {
294                     mCurrentAmplitude = -1;
295                     updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
296                 }
297                 return duration;
298             }
299         } finally {
300             Trace.traceEnd(TRACE_TAG_VIBRATOR);
301         }
302     }
303 
304     /**
305      * Plays vendor vibration effect, using {@code vibrationId} for completion callback to
306      * {@link OnVibrationCompleteListener}.
307      *
308      * <p>This will affect the state of {@link #isVibrating()}.
309      *
310      * @return The positive duration of the vibration started, if successful, zero if the vibrator
311      * do not support the input or a negative number if the operation failed.
312      */
on(VibrationEffect.VendorEffect vendorEffect, long vibrationId, long stepId)313     public long on(VibrationEffect.VendorEffect vendorEffect, long vibrationId, long stepId) {
314         Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (vendor)");
315         synchronized (mLock) {
316             Parcel vendorData = Parcel.obtain();
317             try {
318                 vendorEffect.getVendorData().writeToParcel(vendorData, /* flags= */ 0);
319                 vendorData.setDataPosition(0);
320                 long duration = mNativeWrapper.performVendorEffect(vendorData,
321                         vendorEffect.getEffectStrength(), vendorEffect.getScale(),
322                         vendorEffect.getAdaptiveScale(), vibrationId, stepId);
323                 if (duration > 0) {
324                     mCurrentAmplitude = -1;
325                     updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
326                 }
327                 return duration;
328             } finally {
329                 vendorData.recycle();
330                 Trace.traceEnd(TRACE_TAG_VIBRATOR);
331             }
332         }
333     }
334 
335     /**
336      * Plays predefined vibration effect, using {@code vibrationId} for completion callback to
337      * {@link OnVibrationCompleteListener}.
338      *
339      * <p>This will affect the state of {@link #isVibrating()}.
340      *
341      * @return The positive duration of the vibration started, if successful, zero if the vibrator
342      * do not support the input or a negative number if the operation failed.
343      */
on(PrebakedSegment prebaked, long vibrationId, long stepId)344     public long on(PrebakedSegment prebaked, long vibrationId, long stepId) {
345         Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (Prebaked)");
346         try {
347             synchronized (mLock) {
348                 long duration = mNativeWrapper.perform(prebaked.getEffectId(),
349                         prebaked.getEffectStrength(), vibrationId, stepId);
350                 if (duration > 0) {
351                     mCurrentAmplitude = -1;
352                     updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
353                 }
354                 return duration;
355             }
356         } finally {
357             Trace.traceEnd(TRACE_TAG_VIBRATOR);
358         }
359     }
360 
361     /**
362      * Plays a composition of vibration primitives, using {@code vibrationId} for completion
363      * callback to {@link OnVibrationCompleteListener}.
364      *
365      * <p>This will affect the state of {@link #isVibrating()}.
366      *
367      * @return The positive duration of the vibration started, if successful, zero if the vibrator
368      * do not support the input or a negative number if the operation failed.
369      */
on(PrimitiveSegment[] primitives, long vibrationId, long stepId)370     public long on(PrimitiveSegment[] primitives, long vibrationId, long stepId) {
371         Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (Primitive)");
372         try {
373             if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
374                 return 0;
375             }
376             synchronized (mLock) {
377                 long duration = mNativeWrapper.compose(primitives, vibrationId, stepId);
378                 if (duration > 0) {
379                     mCurrentAmplitude = -1;
380                     updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
381                 }
382                 return duration;
383             }
384         } finally {
385             Trace.traceEnd(TRACE_TAG_VIBRATOR);
386         }
387     }
388 
389     /**
390      * Plays a composition of pwle primitives, using {@code vibrationId} for completion callback
391      * to {@link OnVibrationCompleteListener}.
392      *
393      * <p>This will affect the state of {@link #isVibrating()}.
394      *
395      * @return The duration of the effect playing, or 0 if unsupported.
396      */
on(RampSegment[] primitives, long vibrationId, long stepId)397     public long on(RampSegment[] primitives, long vibrationId, long stepId) {
398         Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (PWLE)");
399         try {
400             if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
401                 return 0;
402             }
403             synchronized (mLock) {
404                 int braking = mVibratorInfo.getDefaultBraking();
405                 long duration = mNativeWrapper.composePwle(
406                         primitives, braking, vibrationId, stepId);
407                 if (duration > 0) {
408                     mCurrentAmplitude = -1;
409                     updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
410                 }
411                 return duration;
412             }
413         } finally {
414             Trace.traceEnd(TRACE_TAG_VIBRATOR);
415         }
416     }
417 
418     /**
419      * Plays a composition of pwle v2 points, using {@code vibrationId} for completion callback
420      * to {@link OnVibrationCompleteListener}.
421      *
422      * <p>This will affect the state of {@link #isVibrating()}.
423      *
424      * @return The duration of the effect playing, or 0 if unsupported.
425      */
on(PwlePoint[] pwlePoints, long vibrationId, long stepId)426     public long on(PwlePoint[] pwlePoints, long vibrationId, long stepId) {
427         Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (PWLE v2)");
428         try {
429             if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2)) {
430                 return 0;
431             }
432             synchronized (mLock) {
433                 long duration = mNativeWrapper.composePwleV2(pwlePoints, vibrationId, stepId);
434                 if (duration > 0) {
435                     mCurrentAmplitude = -1;
436                     updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
437                 }
438                 return duration;
439             }
440         } finally {
441             Trace.traceEnd(TRACE_TAG_VIBRATOR);
442         }
443     }
444 
445     /**
446      * Turns off the vibrator and disables completion callback to any pending vibration.
447      *
448      * <p>This will affect the state of {@link #isVibrating()}.
449      */
off()450     public void off() {
451         Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#off");
452         try {
453             synchronized (mLock) {
454                 mNativeWrapper.off();
455                 mCurrentAmplitude = 0;
456                 updateStateAndNotifyListenersLocked(VibratorState.IDLE);
457             }
458         } finally {
459             Trace.traceEnd(TRACE_TAG_VIBRATOR);
460         }
461     }
462 
463     /**
464      * Resets the vibrator hardware to a default state.
465      * This turns the vibrator off, which will affect the state of {@link #isVibrating()}.
466      */
reset()467     public void reset() {
468         setExternalControl(false);
469         off();
470     }
471 
472     @Override
toString()473     public String toString() {
474         return "VibratorController{"
475                 + "mVibratorInfo=" + mVibratorInfo
476                 + ", mVibratorInfoLoadSuccessful=" + mVibratorInfoLoadSuccessful
477                 + ", mCurrentState=" + mCurrentState.name()
478                 + ", mCurrentAmplitude=" + mCurrentAmplitude
479                 + ", mVibratorStateListeners count="
480                 + mVibratorStateListeners.getRegisteredCallbackCount()
481                 + '}';
482     }
483 
dump(IndentingPrintWriter pw)484     void dump(IndentingPrintWriter pw) {
485         pw.println("Vibrator (id=" + mVibratorInfo.getId() + "):");
486         pw.increaseIndent();
487         pw.println("currentState = " + mCurrentState.name());
488         pw.println("currentAmplitude = " + mCurrentAmplitude);
489         pw.println("vibratorInfoLoadSuccessful = " + mVibratorInfoLoadSuccessful);
490         pw.println("vibratorStateListener size = "
491                 + mVibratorStateListeners.getRegisteredCallbackCount());
492         mVibratorInfo.dump(pw);
493         pw.decreaseIndent();
494     }
495 
496     /**
497      * Updates current vibrator state and notify listeners if {@link #isVibrating()} result changed.
498      */
499     @GuardedBy("mLock")
updateStateAndNotifyListenersLocked(VibratorState state)500     private void updateStateAndNotifyListenersLocked(VibratorState state) {
501         boolean previousIsVibrating = isVibrating(mCurrentState);
502         final boolean newIsVibrating = isVibrating(state);
503         mCurrentState = state;
504         if (previousIsVibrating != newIsVibrating) {
505             // The broadcast method is safe w.r.t. register/unregister listener methods, but lock
506             // is required here to guarantee delivery order.
507             mVibratorStateListeners.broadcast(
508                     listener -> notifyStateListener(listener, newIsVibrating));
509         }
510     }
511 
notifyStateListener(IVibratorStateListener listener, boolean isVibrating)512     private void notifyStateListener(IVibratorStateListener listener, boolean isVibrating) {
513         try {
514             listener.onVibrating(isVibrating);
515         } catch (RemoteException | RuntimeException e) {
516             Slog.e(TAG, "Vibrator state listener failed to call", e);
517         }
518     }
519 
520     /** Returns true only if given state is not {@link VibratorState#IDLE}. */
isVibrating(VibratorState state)521     private static boolean isVibrating(VibratorState state) {
522         return state != VibratorState.IDLE;
523     }
524 
525     /** Wrapper around the static-native methods of {@link VibratorController} for tests. */
526     @VisibleForTesting
527     public static class NativeWrapper {
528         /**
529          * Initializes the native part of this controller, creating a global reference to given
530          * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This
531          * wrapper is responsible for deleting this pointer by calling the method pointed
532          * by {@link #getNativeFinalizer()}.
533          *
534          * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener}
535          * do not hold any strong reference to the instance responsible for deleting the returned
536          * pointer, to avoid creating a cyclic GC root reference.
537          */
nativeInit(int vibratorId, OnVibrationCompleteListener listener)538         private static native long nativeInit(int vibratorId, OnVibrationCompleteListener listener);
539 
540         /**
541          * Returns pointer to native function responsible for cleaning up the native pointer
542          * allocated and returned by {@link #nativeInit(int, OnVibrationCompleteListener)}.
543          */
getNativeFinalizer()544         private static native long getNativeFinalizer();
545 
isAvailable(long nativePtr)546         private static native boolean isAvailable(long nativePtr);
547 
on(long nativePtr, long milliseconds, long vibrationId, long stepId)548         private static native long on(long nativePtr, long milliseconds, long vibrationId,
549                 long stepId);
550 
off(long nativePtr)551         private static native void off(long nativePtr);
552 
setAmplitude(long nativePtr, float amplitude)553         private static native void setAmplitude(long nativePtr, float amplitude);
554 
performEffect(long nativePtr, long effect, long strength, long vibrationId, long stepId)555         private static native long performEffect(long nativePtr, long effect, long strength,
556                 long vibrationId, long stepId);
557 
performVendorEffect(long nativePtr, Parcel vendorData, long strength, float scale, float adaptiveScale, long vibrationId, long stepId)558         private static native long performVendorEffect(long nativePtr, Parcel vendorData,
559                 long strength, float scale, float adaptiveScale, long vibrationId, long stepId);
560 
performComposedEffect(long nativePtr, PrimitiveSegment[] effect, long vibrationId, long stepId)561         private static native long performComposedEffect(long nativePtr, PrimitiveSegment[] effect,
562                 long vibrationId, long stepId);
563 
performPwleEffect(long nativePtr, RampSegment[] effect, int braking, long vibrationId, long stepId)564         private static native long performPwleEffect(long nativePtr, RampSegment[] effect,
565                 int braking, long vibrationId, long stepId);
566 
performPwleV2Effect(long nativePtr, PwlePoint[] effect, long vibrationId, long stepId)567         private static native long performPwleV2Effect(long nativePtr, PwlePoint[] effect,
568                 long vibrationId, long stepId);
569 
setExternalControl(long nativePtr, boolean enabled)570         private static native void setExternalControl(long nativePtr, boolean enabled);
571 
alwaysOnEnable(long nativePtr, long id, long effect, long strength)572         private static native void alwaysOnEnable(long nativePtr, long id, long effect,
573                 long strength);
574 
alwaysOnDisable(long nativePtr, long id)575         private static native void alwaysOnDisable(long nativePtr, long id);
576 
getInfo(long nativePtr, VibratorInfo.Builder infoBuilder)577         private static native boolean getInfo(long nativePtr, VibratorInfo.Builder infoBuilder);
578 
579         private long mNativePtr = 0;
580 
581         /** Initializes native controller and allocation registry to destroy native instances. */
init(int vibratorId, OnVibrationCompleteListener listener)582         public void init(int vibratorId, OnVibrationCompleteListener listener) {
583             mNativePtr = nativeInit(vibratorId, listener);
584             long finalizerPtr = getNativeFinalizer();
585 
586             if (finalizerPtr != 0) {
587                 NativeAllocationRegistry registry =
588                         NativeAllocationRegistry.createMalloced(
589                                 VibratorController.class.getClassLoader(), finalizerPtr);
590                 registry.registerNativeAllocation(this, mNativePtr);
591             }
592         }
593 
594         /** Check if the vibrator is currently available. */
isAvailable()595         public boolean isAvailable() {
596             return isAvailable(mNativePtr);
597         }
598 
599         /** Turns vibrator on for given time. */
on(long milliseconds, long vibrationId, long stepId)600         public long on(long milliseconds, long vibrationId, long stepId) {
601             return on(mNativePtr, milliseconds, vibrationId, stepId);
602         }
603 
604         /** Turns vibrator off. */
off()605         public void off() {
606             off(mNativePtr);
607         }
608 
609         /** Sets the amplitude for the vibrator to run. */
setAmplitude(float amplitude)610         public void setAmplitude(float amplitude) {
611             setAmplitude(mNativePtr, amplitude);
612         }
613 
614         /** Turns vibrator on to perform one of the supported effects. */
perform(long effect, long strength, long vibrationId, long stepId)615         public long perform(long effect, long strength, long vibrationId, long stepId) {
616             return performEffect(mNativePtr, effect, strength, vibrationId, stepId);
617         }
618 
619         /** Turns vibrator on to perform a vendor-specific effect. */
performVendorEffect(Parcel vendorData, long strength, float scale, float adaptiveScale, long vibrationId, long stepId)620         public long performVendorEffect(Parcel vendorData, long strength, float scale,
621                 float adaptiveScale, long vibrationId, long stepId) {
622             return performVendorEffect(mNativePtr, vendorData, strength, scale, adaptiveScale,
623                     vibrationId, stepId);
624         }
625 
626         /** Turns vibrator on to perform effect composed of give primitives effect. */
compose(PrimitiveSegment[] primitives, long vibrationId, long stepId)627         public long compose(PrimitiveSegment[] primitives, long vibrationId, long stepId) {
628             return performComposedEffect(mNativePtr, primitives, vibrationId, stepId);
629         }
630 
631         /** Turns vibrator on to perform PWLE effect composed of given primitives. */
composePwle(RampSegment[] primitives, int braking, long vibrationId, long stepId)632         public long composePwle(RampSegment[] primitives, int braking, long vibrationId,
633                 long stepId) {
634             return performPwleEffect(mNativePtr, primitives, braking, vibrationId, stepId);
635         }
636 
637         /** Turns vibrator on to perform PWLE effect composed of given points. */
composePwleV2(PwlePoint[] pwlePoints, long vibrationId, long stepId)638         public long composePwleV2(PwlePoint[] pwlePoints, long vibrationId, long stepId) {
639             return performPwleV2Effect(mNativePtr, pwlePoints, vibrationId, stepId);
640         }
641 
642         /** Enabled the device vibrator to be controlled by another service. */
setExternalControl(boolean enabled)643         public void setExternalControl(boolean enabled) {
644             setExternalControl(mNativePtr, enabled);
645         }
646 
647         /** Enable always-on vibration with given id and effect. */
alwaysOnEnable(long id, long effect, long strength)648         public void alwaysOnEnable(long id, long effect, long strength) {
649             alwaysOnEnable(mNativePtr, id, effect, strength);
650         }
651 
652         /** Disable always-on vibration for given id. */
alwaysOnDisable(long id)653         public void alwaysOnDisable(long id) {
654             alwaysOnDisable(mNativePtr, id);
655         }
656 
657         /**
658          * Loads device vibrator metadata and returns true if all metadata was loaded successfully.
659          */
getInfo(VibratorInfo.Builder infoBuilder)660         public boolean getInfo(VibratorInfo.Builder infoBuilder) {
661             return getInfo(mNativePtr, infoBuilder);
662         }
663     }
664 }
665