Android Vibrator

Vibrator框架
APP简单震动实现
一定要在AndroidManifest.xml中注册

// 震动效果的系统服务
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.Creator<OneShot> CREATOR =
    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 VibrationEffect createOneShot(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 void vibrate(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 void vibratorOn(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 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 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,然后重新开启定时器,倒计时的时间就是写入节点的值,到时间再把马达关闭就好。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北境王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值