Android Vibrator

APP简单震动实现

一定要在AndroidManifest.xml中注册

<uses-permission android:name="android.permission.VIBRATE" />

// 震动效果的系统服务

vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);

VibratorService来源:

在SystemServer启动的时startOtherServices()中将VibratorService 添加至ServiceManager

vibrator = new VibratorService(context);

ServiceManager.addService("vibrator", vibrator);

震动需要调用的方法有:

cancel()关闭或者停止振动器。

hasVibrator()判断硬件是否有振动器。

vibrate(long milliseconds)控制手机振动时长(毫秒)

vibrate(long[] pattern,int repeat)指定手机以pattern指定的模式振动。 参数1:是一个数组,里面的数字下标为单数时,为停止(等待)时间,偶数时为震动时间,比如 [100,100] 就为停止100ms后震动100ms,参数2 repeat:重复次数,如果是-1的只振动一次,如果是0的话则一直振动 。

然后就可以根据需要,调用这些方法了

比如,我们让手机让手机震动3秒就可以这么做

if (vibrator.hasVibrator()){

    effect = VibrationEffect.createOneShot(3000, VibrationEffect.DEFAULT_AMPLITUDE);

    vibrator.vibrate(effect);

}else {

    Log.e("TOP","该设备没有震动器");

}

vibrator.vibrate(200);//振动0.2秒,这种调用方法震动也是可以的,不过已经用的越来越少了

既然VibrationEffect方式用的多了,我们不妨来看一下VibrationEffect这个类

VibrationEffect.OneShot  // 一次性的震动

VibrationEffect.Waveform // 波形震动

VibrationEffect.Prebaked // 预设的震动形式

这三种震动反馈被抽象为VibrationEffect,

三种震动反馈 OneShot,Waveform,Prebaked

都继承自VibrationEffect。

eg: OneShot

/** @hide */

@TestApi

public static class OneShot extends VibrationEffect implements Parcelable {

    private final long mDuration;

    private final int mAmplitude;

    public OneShot(Parcel in) {

        mDuration = in.readLong();

        mAmplitude = in.readInt();

    }

    public OneShot(long milliseconds, int amplitude) {

        mDuration = milliseconds;

        mAmplitude = amplitude;

    }

    @Override

    public long getDuration() {

        return mDuration;

    }

    public int getAmplitude() {

        return mAmplitude;

    }

/**

    * Scale the amplitude of this effect.

    *

    * @param gamma the gamma adjustment to apply

    * @param maxAmplitude the new maximum amplitude of the effect, must be between 0 and

    *        MAX_AMPLITUDE

    * @throws IllegalArgumentException if maxAmplitude less than 0 or more than MAX_AMPLITUDE

    *

    * @return A {@link OneShot} effect with the same timing but scaled amplitude.

    */

    public OneShot scale(float gamma, int maxAmplitude) {

if (maxAmplitude >MAX_AMPLITUDE || maxAmplitude < 0) {

            throw new IllegalArgumentException(

                    "Amplitude is negative or greater than MAX_AMPLITUDE");

        }

int newAmplitude =scale(mAmplitude, gamma, maxAmplitude);

        return new OneShot(mDuration, newAmplitude);

    }

/**

    * Resolve default values into integer amplitude numbers.

    *

    * @param defaultAmplitude the default amplitude to apply, must be between 0 and

    *        MAX_AMPLITUDE

    * @return A {@link OneShot} effect with same physical meaning but explicitly set amplitude

    *

    * @hide

    */

    public OneShot resolve(int defaultAmplitude) {

if (defaultAmplitude >MAX_AMPLITUDE || defaultAmplitude < 0) {

            throw new IllegalArgumentException(

                    "Amplitude is negative or greater than MAX_AMPLITUDE");

        }

if (mAmplitude ==DEFAULT_AMPLITUDE) {

            return new OneShot(mDuration, defaultAmplitude);

        }

        return this;

    }

    @Override

    public void validate() {

        if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) {

            throw new IllegalArgumentException(

                    "amplitude must either be DEFAULT_AMPLITUDE, "

                    + "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");

        }

        if (mDuration <= 0) {

            throw new IllegalArgumentException(

                    "duration must be positive (duration=" + mDuration + ")");

        }

    }

    @Override

    public boolean equals(Object o) {

        if (!(o instanceof VibrationEffect.OneShot)) {

            return false;

        }

        VibrationEffect.OneShot other = (VibrationEffect.OneShot) o;

        return other.mDuration == mDuration && other.mAmplitude == mAmplitude;

    }

    @Override

    public int hashCode() {

        int result = 17;

        result += 37 * (int) mDuration;

        result += 37 * mAmplitude;

        return result;

    }

    @Override

    public String toString() {

        return "OneShot{mDuration=" + mDuration + ", mAmplitude=" + mAmplitude + "}";

    }

    @Override

    public void writeToParcel(Parcel out, int flags) {

out.writeInt(PARCEL_TOKEN_ONE_SHOT);

        out.writeLong(mDuration);

        out.writeInt(mAmplitude);

    }

    @UnsupportedAppUsage

public static final @android.annotation.NonNull Parcelable.CreatorCREATOR =

        new Parcelable.Creator<OneShot>() {

            @Override

            public OneShot createFromParcel(Parcel in) {

                // Skip the type token

                in.readInt();

                return new OneShot(in);

            }

            @Override

            public OneShot[] newArray(int size) {

                return new OneShot[size];

            }

        };

}

VibratorEffect创造一个 OneShot震动

public static VibrationEffectcreateOneShot(long milliseconds, int amplitude) {

if (VibrateUtils.canConvertToPrebaked(milliseconds, ActivityThread.currentPackageName())) {

if (amplitude ==DEFAULT_AMPLITUDE) amplitude = 255;

return VibrateUtils.toPrebakedEffectByDur(milliseconds, amplitude);

    }

    VibrationEffect effect = new OneShot(milliseconds, amplitude);

    effect.validate();

    return effect;

}

因此可以用一下方式启动震动

effect = VibrationEffect.createOneShot(3000, VibrationEffect.DEFAULT_AMPLITUDE);

vibrator.vibrate(effect);

间歇性震动如下:

以前常用方式vibrate(long[] pattern,int repeat)

现在多用方式VibrationEffect

if (vibrator.hasVibrator()){

      vibrator.cancel();

      effect = VibrationEffect.createWaveform(new long[]{100,100}, 0);

      vibrator.vibrate(effect);

  }else {

      Log.e("TOP","该设备没有震动器");

  }

Android S ,有很多改动包括VibratorService.java

现在从简单的马达框架入手进行讲解。

以Android 9为例子

整个马达框架比较简单,安卓官方已经帮我们实现了framework到HAL层,下面实现的就只有驱动层。接下来我们梳理一下从上层到底层怎么流程。

1、APP层

import android.os.Vibrator;

import android.widget.ToggleButton;

public class MainActivity extends Activity {


        @Override

        protected void onCreate(Bundle savedInstanceState) {


                super.onCreate(savedInstanceState);

                setContentView(R.layout.activity_main);


                private Vibrator vibrator=null;

                vibrator=(Vibrator)this.getSystemService(VIBRATOR_SERVICE);

                toggleButton1=(ToggleButton)findViewById(R.id.toggleButton1);

                /*短震动*/

                toggleButton1.setOnCheckedChangeListener(new ToggleButton.OnCheckedChangeListener() {



          @Override

          public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {


                  if(isChecked){

                        Log.i(TAG,"toggleButton1 enter vibrator.vibrate");

                        //设置震动周期,第二个参数为 -1表示只震动一次

vibrator.vibrate(new long[]{1000, 10, 100, 1000},-1);

                    }else{

                        //取消震动

                        Log.i(TAG,"toggleButton1 enter vibrator.cancel()");

                        vibrator.cancel();

                    }

            }

        });

    }

}

上面展示了一个最简单的马达震动应用代码,获得服务后即可调用接口进行驱动。

2、framework层

代码路径:frameworks\base\services\core\java\com\android\server\VibratorService.java

  @Override // Binder call

public voidvibrate(int uid, String opPkg, VibrationEffect effect, int usageHint,

            IBinder token) {


        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate");

        try {


            if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)

                    != PackageManager.PERMISSION_GRANTED) {


                throw new SecurityException("Requires VIBRATE permission");

            }

            if (token == null) {


                Slog.e(TAG, "token must not be null");

                return;

            }

            verifyIncomingUid(uid);

            if (!verifyVibrationEffect(effect)) {


                return;

            }

            // If our current vibration is longer than the new vibration and is the same amplitude,

            // then just let the current one finish.

            synchronized (mLock) {


                if (effect instanceof VibrationEffect.OneShot

                        && mCurrentVibration != null

                        && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {


                    VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;

                    VibrationEffect.OneShot currentOneShot =

                            (VibrationEffect.OneShot) mCurrentVibration.effect;

                    if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())

                            && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {


                        if (DEBUG) {


                            Slog.d(TAG,

                                    "Ignoring incoming vibration in favor of current vibration");

                        }

                        return;

                    }

                }

                // If the current vibration is repeating and the incoming one is non-repeating,

                // then ignore the non-repeating vibration. This is so that we don't cancel

                // vibrations that are meant to grab the attention of the user, like ringtones and

                // alarms, in favor of one-shot vibrations that are likely quite short.

                if (!isRepeatingVibration(effect)

                        && mCurrentVibration != null

                        && isRepeatingVibration(mCurrentVibration.effect)) {


                    if (DEBUG) {


                        Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");

                    }

                    return;

                }

                Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);

                linkVibration(vib);

                long ident = Binder.clearCallingIdentity();

                try {


                    doCancelVibrateLocked();

                    startVibrationLocked(vib);

                    addToPreviousVibrationsLocked(vib);

                } finally {

                    Binder.restoreCallingIdentity(ident);

                }

            }

        } finally {

            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);

        }

    }

接口里面会判断一下权限,根据应用层传递的不同effect值,有不同的震动效果。然后就调用到JNI层,调用顺序大概如下:

startVibrationLocked

        startVibrationInnerLocked

                doVibratorOn

vibratorOn

3、JNI层

代码路径:frameworks\base\services\core\jni\com_android_server_VibratorService.cpp

static voidvibratorOn(JNIEnv* /* env */, jobject /* clazz */, jlong timeout_ms)

{


    Status retStatus = halCall(&V1_0::IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR);

    if (retStatus != Status::OK) {


        ALOGE("vibratorOn command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));

    }

}

static void vibratorOff(JNIEnv* /* env */, jobject /* clazz */)

{


    Status retStatus = halCall(&V1_0::IVibrator::off).withDefault(Status::UNKNOWN_ERROR);

    if (retStatus != Status::OK) {


        ALOGE("vibratorOff command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));

    }

}

static const JNINativeMethod method_table[] = {


    {

    "vibratorExists", "()Z", (void*)vibratorExists },

    {

    "vibratorInit", "()V", (void*)vibratorInit },

    {

    "vibratorOn", "(J)V", (void*)vibratorOn },

    {

    "vibratorOff", "()V", (void*)vibratorOff },

    {

    "vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},

    {

    "vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},

    {

    "vibratorPerformEffect", "(JJ)J", (void*)vibratorPerformEffect}

};

int register_android_server_VibratorService(JNIEnv *env)

{


    return jniRegisterNativeMethods(env, "com/android/server/VibratorService",

            method_table, NELEM(method_table));

}

以马达的On和off为例,会调用到HAL层的on和off方法。

4、HIDL层

代码路径:hardware\interfaces\vibrator\1.0\default\Vibrator.cpp

Return<Status> Vibrator::on(uint32_t timeout_ms) {


    int32_t ret = mDevice->vibrator_on(mDevice, timeout_ms);

    if (ret != 0) {


        ALOGE("on command failed : %s", strerror(-ret));

        return Status::UNKNOWN_ERROR;

    }

    return Status::OK;

}

Return<Status> Vibrator::off()  {


    int32_t ret = mDevice->vibrator_off(mDevice);

    if (ret != 0) {


        ALOGE("off command failed : %s", strerror(-ret));

        return Status::UNKNOWN_ERROR;

    }

    return Status::OK;

}

HIDL层是较新的安卓版本才引入的,是连接HAL层和JNI层的桥梁。

5、HAL层

代码路径:hardware\libhardware\modules\vibrator\vibrator.c

static const char THE_DEVICE[] = "/sys/class/timed_output/vibrator/enable";

static int sendit(unsigned int timeout_ms)

{


    char value[TIMEOUT_STR_LEN]; /* large enough for millions of years */

    snprintf(value, sizeof(value), "%u", timeout_ms);

    return write_value(THE_DEVICE, value);

}

static int vibra_on(vibrator_device_t* vibradev __unused, unsigned int timeout_ms)

{


    /* constant on, up to maximum allowed time */

    return sendit(timeout_ms);

}

static int vibra_off(vibrator_device_t* vibradev __unused)

{


    return sendit(0);

}

static int vibra_open(const hw_module_t* module, const char* id __unused,

                      hw_device_t** device __unused) {


    bool use_led;

    if (vibra_exists()) {


        ALOGD("Vibrator using timed_output");

        use_led = false;

    } else if (vibra_led_exists()) {


        ALOGD("Vibrator using LED trigger");

        use_led = true;

    } else {


        ALOGE("Vibrator device does not exist. Cannot start vibrator");

        return -ENODEV;

    }

    vibrator_device_t *vibradev = calloc(1, sizeof(vibrator_device_t));

    if (!vibradev) {


        ALOGE("Can not allocate memory for the vibrator device");

        return -ENOMEM;

    }

    vibradev->common.tag = HARDWARE_DEVICE_TAG;

    vibradev->common.module = (hw_module_t *) module;

    vibradev->common.version = HARDWARE_DEVICE_API_VERSION(1,0);

    vibradev->common.close = vibra_close;

    if (use_led) {


        vibradev->vibrator_on = vibra_led_on;

        vibradev->vibrator_off = vibra_led_off;

    } else {


        vibradev->vibrator_on = vibra_on;

        vibradev->vibrator_off = vibra_off;

    }

    *device = (hw_device_t *) vibradev;

    return 0;

}

其实开启和关闭马达的工作很简单,就是往节点"/sys/class/timed_output/vibrator/enable"写入震动时间,所以可以想得到驱动层只需要提供一个节点供上层操作就好。

6、驱动层

马达的驱动是基于kernel提供的timed_output框架完成的:

代码路径:kernel-4.4\drivers\staging\android\timed_output.c

代码比较简单,提供接口给驱动在"/sys/class/timed_output/"路径下面建立自己的节点,并提供节点的device attribute的操作接口,当我们写节点的时候就会调用到enable_store函数,并调用注册驱动的enable函数

static struct class *timed_output_class;

static ssize_t enable_store(struct device *dev, struct device_attribute *attr,

                            const char *buf, size_t size)

{


        struct timed_output_dev *tdev = dev_get_drvdata(dev);

        int value;

        int rc;

        rc = kstrtoint(buf, 0, &value);

        if (rc != 0)

                return -EINVAL;

        tdev->enable(tdev, value);

        return size;

}

static DEVICE_ATTR_RW(enable);

static struct attribute *timed_output_attrs[] = {


        &dev_attr_enable.attr,

        NULL,

};

ATTRIBUTE_GROUPS(timed_output);

static int create_timed_output_class(void)

{


        if (!timed_output_class) {


                timed_output_class = class_create(THIS_MODULE, "timed_output");

                if (IS_ERR(timed_output_class))

                        return PTR_ERR(timed_output_class);

                atomic_set(&device_count, 0);

                timed_output_class->dev_groups = timed_output_groups;

        }

        return 0;

}

int timed_output_dev_register(struct timed_output_dev *tdev)

{


        int ret;

        if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time)

                return -EINVAL;

        ret = create_timed_output_class();

        if (ret < 0)

                return ret;

        tdev->index = atomic_inc_return(&device_count);

        tdev->dev = device_create(timed_output_class, NULL,

                MKDEV(0, tdev->index), NULL, "%s", tdev->name);

        if (IS_ERR(tdev->dev))

                return PTR_ERR(tdev->dev);

        dev_set_drvdata(tdev->dev, tdev);

        tdev->state = 0;

        return 0;

}

现在我们看一下基于上面框架书写的马达驱动:

static void vibrator_off(void)

{


        gpio_direction_output(gpio, !en_value);     

        wake_unlock(&vibdata.wklock); //震动关闭就可以释放 wake_lock锁       

}

void motor_enable(struct timed_output_dev *sdev,int value)

{


        mutex_lock(&vibdata.lock); //关键代码段,同一时间只允许一个线程执行

        /* cancelprevious timer and set GPIO according to value */

        hrtimer_cancel(&vibdata.timer); //当先前定时器完成后 关闭这个定时器

        cancel_work_sync(&vibdata.work); //当上次震动完成后 关闭这次动作

        if(value)

        {


                wake_lock(&vibdata.wklock); //开始震动打开wake lock锁不允许休眠

                gpio_direction_output(gpio, en_value);

                if(value > 0)

                {


                        if(value > MAX_TIMEOUT)

                                value= MAX_TIMEOUT;

                        hrtimer_start(&vibdata.timer,

                                ktime_set(value / 1000, (value % 1000) * 1000000),

                                HRTIMER_MODE_REL);

                }

        }

        else

                vibrator_off();

        mutex_unlock(&vibdata.lock);

}

struct timed_output_dev motot_driver = {


        .name ="vibrator", //注意这个名字,由于HAL层里面的设备为//"/sys/class/timed_output/vibrator/enable"

                          //因此这个名字必须为"vibrator"

        .enable= motor_enable,

        .get_time= get_time,

};

static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) //定时器结束时候的回调函数

{


        schedule_work(&vibdata.work); //定时器完成了 执行work队列回调函数来关闭电机

        return HRTIMER_NORESTART;

}

static void vibrator_work(struct work_struct *work)

{


        vibrator_off();

}

static int motor_probe(struct platform_device *pdev)

{


        struct device_node *node = pdev->dev.of_node;

        enum of_gpio_flags flags;

        int ret =0;


        hrtimer_init(&vibdata.timer,CLOCK_MONOTONIC, HRTIMER_MODE_REL);

        vibdata.timer.function= vibrator_timer_func;

        INIT_WORK(&vibdata.work,vibrator_work);

        ...


        ret=timed_output_dev_register(&motot_driver);

        if (ret< 0)

                goto err_to_dev_reg;

        return 0;

err_to_dev_reg:

        mutex_destroy(&vibdata.lock);

        wake_lock_destroy(&vibdata.wklock);

        printk("vibrator  err!:%d\n",ret);

        return ret;

}

1、驱动接受上层传递震动时长

驱动接收上层传递过来的是震动时长,单位为毫秒。在驱动里注册一个定时器,定时器倒计时到期后会唤醒注册的工作队列,最终会执行vibrator_work()函数去关闭马达震动。

2、马达驱动注册

调用timed_output框架提供的timed_output_dev_register()接口将我们的马达驱动注册进系统,这里的关键就是我们需要自定义struct timed_output_dev结构体,填充enable和get_time函数。enable函数用来开启马达的震动:

void motor_enable(struct timed_output_dev *sdev,int value)

{


        mutex_lock(&vibdata.lock); //关键代码段,同一时间只允许一个线程执行

        /* cancelprevious timer and set GPIO according to value */

        hrtimer_cancel(&vibdata.timer); //当先前定时器完成后 关闭这个定时器

        cancel_work_sync(&vibdata.work); //当上次震动完成后 关闭这次动作

        if(value)

        {


                wake_lock(&vibdata.wklock); //开始震动打开wake lock锁不允许休眠

                gpio_direction_output(gpio, en_value);

                if(value > 0)

                {


                        if(value > MAX_TIMEOUT)

                                value= MAX_TIMEOUT;

                        hrtimer_start(&vibdata.timer,

                                ktime_set(value / 1000, (value % 1000) * 1000000),

                                HRTIMER_MODE_REL);

                }

        }

        else

                vibrator_off();

        mutex_unlock(&vibdata.lock);

}

开启震动的操作也很简单,只是写一下GPIO,然后重新开启定时器,倒计时的时间就是写入节点的值,到时间再把马达关闭就好。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容