文档章节

Android & Vibrator

小强的幻音宝盒
 小强的幻音宝盒
发布于 2013/04/24 22:42
字数 1778
阅读 3K
收藏 10

「深度学习福利」大神带你进阶工程师,立即查看>>>

    Android振动器 android.os.Vibrator

1.Java层 frameworks/base/core/java/android/os/Vibrator.java

  Vibrator类的实力可以通过 getSystemService(VIBRATOR_SERVICE)获得实例。
 两个方法:public abstract void vibrate(long milliseconds);/public abstract void vibrate(long[] pattern, int repeat);
               public abstract void cancel();
查找路径:Activity.java-->ContextThemeWrapper.java-->ContextWrapper.java-->Context.java,getSystemService是Context.java类的抽象方法。其实现的类是ContextImpl.java,其中有段静态代码,注册所有context下能使用到的service。

 registerService(VIBRATOR_SERVICE, new ServiceFetcher() {
                public Object createService(ContextImpl ctx) {
                    return new SystemVibrator();
                }}); 

Vibrator类只是个抽象类,具体实现的代码位于 SystemVibrator。位于\frameworks\base\core\java\android\os\目录下。两个方法的实现代码如下:

@Override
    public void vibrate(long milliseconds) {
        if (mService == null) {
            Log.w(TAG, "Failed to vibrate; no vibrator service.");
            return;
        }
        try {
            mService.vibrate(milliseconds, mToken);
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to vibrate.", e);
        }
    }

@Override
    public void vibrate(long[] pattern, int repeat) {
        if (mService == null) {
            Log.w(TAG, "Failed to vibrate; no vibrator service.");
            return;
        }
        // catch this here because the server will do nothing.  pattern may
        // not be null, let that be checked, because the server will drop it
        // anyway
        if (repeat < pattern.length) {
            try {
                mService.vibratePattern(pattern, repeat, mToken);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed to vibrate.", e);
            }
        } else {
            throw new ArrayIndexOutOfBoundsException();
        }
    }

还有

@Override
    public void cancel() {
        if (mService == null) {
            return;
        }
        try {
            mService.cancelVibrate(mToken);
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to cancel vibration.", e);
        }
    }

  查看mService,其声明代码如下:

 

mService = IVibratorService.Stub.asInterface(
                ServiceManager.getService("vibrator"));


 /*这里跟Binder机制有关。。。断片ing*/

 

定义是private final IVibratorService mService;

所以找找这个IVibratorService吧。
查找下\frameworks\base\core\java\android\os\IVibratorService.aidl,这里定义了接口。

package android.os;

/** {@hide} */
interface IVibratorService
{
    boolean hasVibrator();
    void vibrate(long milliseconds, IBinder token);
    void vibratePattern(in long[] pattern, int repeat, IBinder token);
    void cancelVibrate(IBinder token);
}


 具体的实现位于VibratorService.java。(\frameworks\base\services\java\com\android\server\)

  public class VibratorService extends IVibratorService.Stub


 
然后是vibrate方法:

public void vibrate(long milliseconds, IBinder token) {
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Requires VIBRATE permission");
        }
        int uid = Binder.getCallingUid();
        // We're running in the system server so we cannot crash. Check for a
        // timeout of 0 or negative. This will ensure that a vibration has
        // either a timeout of > 0 or a non-null pattern.
        if (milliseconds <= 0 || (mCurrentVibration != null
                && mCurrentVibration.hasLongerTimeout(milliseconds))) {
            // Ignore this vibration since the current vibration will play for
            // longer than milliseconds.
            return;
        }

        Vibration vib = new Vibration(token, milliseconds, uid);
        synchronized (mVibrations) {
            removeVibrationLocked(token);
            doCancelVibrateLocked();
            mCurrentVibration = vib;
            startVibrationLocked(vib);
        }
    }
         然后是startVibrationLocked方法。
private void startVibrationLocked(final Vibration vib) {
        if (vib.mTimeout != 0) {
            doVibratorOn(vib.mTimeout);
            mH.postDelayed(mVibrationRunnable, vib.mTimeout);
        } else {
            // mThread better be null here. doCancelVibrate should always be
            // called before startNextVibrationLocked or startVibrationLocked.
            mThread = new VibrateThread(vib);
            mThread.start();
        }
    }
       翻看了VibrateThread的run方法,最终还是调用了doVibratorOn方法。
       所以看doVibratorOn方法:
private void doVibratorOn(long millis) {
        synchronized (mInputDeviceVibrators) {
            final int vibratorCount = mInputDeviceVibrators.size();
            if (vibratorCount != 0) {
                for (int i = 0; i < vibratorCount; i++) {
                    mInputDeviceVibrators.get(i).vibrate(millis);
                }
            } else {
                vibratorOn(millis);
            }
        }
    }
          |---------首先是vibratorCount这个,假如这个不为0,那么是调用 Vibrator的vibrate方法。。断片:vibratorCount这个表示什么意义!
          mInputDeviceVibrators的声明如下:
private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
     里面的每一个元素都是一个Vibrator的实力,Vibrator是个抽闲类。
     mInputDeviceVibrators会在VibratorService.java中的updateInputDeviceVibrators方法添加元素:
mInputDeviceVibrators.clear();
if (mVibrateInputDevicesSetting) {
	int[] ids = mIm.getInputDeviceIds();
	for (int i = 0; i < ids.length; i++) {
		InputDevice device = mIm.getInputDevice(ids[i]);
		Vibrator vibrator = device.getVibrator();
		if (vibrator.hasVibrator()) {
			mInputDeviceVibrators.add(vibrator);
		}
	}
}
      查看InputDevice类的getVibrator
public Vibrator getVibrator() {
        synchronized (mMotionRanges) {
            if (mVibrator == null) {
                if (mHasVibrator) {
                    mVibrator = InputManager.getInstance().getInputDeviceVibrator(mId);
                } else {
                    mVibrator = NullVibrator.getInstance();
                }
            }
            return mVibrator;
        }
    }
所以数组 mInputDeviceVibrators中的元素有两种,都是抽象类Vibrator的子类:
        一种是InputDeviceVibrator类,一种就是NullVibrator类了。
      先说说InputDeviceVibrator类。
private final class InputDeviceVibrator extends Vibrator {
        private final int mDeviceId;
        private final Binder mToken;

        public InputDeviceVibrator(int deviceId) {
            mDeviceId = deviceId;
            mToken = new Binder();
        }

        @Override
        public boolean hasVibrator() {
            return true;
        }

        @Override
        public void vibrate(long milliseconds) {
            vibrate(new long[] { 0, milliseconds}, -1);
        }

        @Override
        public void vibrate(long[] pattern, int repeat) {
            if (repeat >= pattern.length) {
                throw new ArrayIndexOutOfBoundsException();
            }
            try {
                mIm.vibrate(mDeviceId, pattern, repeat, mToken);
            } catch (RemoteException ex) {
                Log.w(TAG, "Failed to vibrate.", ex);
            }
        }

        @Override
        public void cancel() {
            try {
                mIm.cancelVibrate(mDeviceId, mToken);
            } catch (RemoteException ex) {
                Log.w(TAG, "Failed to cancel vibration.", ex);
            }
        }
    }
       这里关注public void vibrate(long[] pattern, int repeat)方法,最终调用mIm.vibrate(mDeviceId, pattern, repeat, mToken)。。坑爹的,mIm是个抽象类,又得去找其实例,然后穿越到InputManagerService.java,放看起vibrate方法:
public void vibrate(int deviceId, long[] pattern, int repeat, IBinder token) {
        if (repeat >= pattern.length) {
            throw new ArrayIndexOutOfBoundsException();
        }

        VibratorToken v;
        synchronized (mVibratorLock) {
            v = mVibratorTokens.get(token);
            if (v == null) {
                v = new VibratorToken(deviceId, token, mNextVibratorTokenValue++);
                try {
                    token.linkToDeath(v, 0);
                } catch (RemoteException ex) {
                    // give up
                    throw new RuntimeException(ex);
                }
                mVibratorTokens.put(token, v);
            }
        }

        synchronized (v) {
            v.mVibrating = true;
            nativeVibrate(mPtr, deviceId, pattern, repeat, v.mTokenValue);
        }
    }
           这里调用了JNI方法nativeVibrate,同样,InputDeviceVibrator的cancel方法也到了这里:
public void cancelVibrate(int deviceId, IBinder token) {
        VibratorToken v;
        synchronized (mVibratorLock) {
            v = mVibratorTokens.get(token);
            if (v == null || v.mDeviceId != deviceId) {
                return; // nothing to cancel
            }
        }

        cancelVibrateIfNeeded(v);
    }
              cancelVibrateIfNeeded方法最终调用nativeCancelVibrate方法。到达JNI层。
              |||||||||
              再来看看NullVibrator类。。不过既然是Null。。。那就是Null实现。
              自此,vibrate跟cancel到达的JNI层的接口是:
private static native void nativeVibrate(int ptr, int deviceId, long[] pattern,
            int repeat, int token);
    private static native void nativeCancelVibrate(int ptr, int deviceId, int token);
       |-------然后是vibratorCount为0的情况,直接调用vibratorOn,cancel同理。
       自此,vibrate跟cancel到达的JNI层的接口是:

native static void vibratorOn(long milliseconds);
    native static void vibratorOff();



2.JNI层
--对于nativeVibrate/nativeCancelVibrate,其实现位于com_android_server_input_InputManagerService.cpp中.

static void nativeVibrate(JNIEnv* env,
        jclass clazz, jint ptr, jint deviceId, jlongArray patternObj,
        jint repeat, jint token) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    size_t patternSize = env->GetArrayLength(patternObj);
    if (patternSize > MAX_VIBRATE_PATTERN_SIZE) {
        ALOGI("Skipped requested vibration because the pattern size is %d "
                "which is more than the maximum supported size of %d.",
                patternSize, MAX_VIBRATE_PATTERN_SIZE);
        return; // limit to reasonable size
    }

    jlong* patternMillis = static_cast<jlong*>(env->GetPrimitiveArrayCritical(
            patternObj, NULL));
    nsecs_t pattern[patternSize];
    for (size_t i = 0; i < patternSize; i++) {
        pattern[i] = max(jlong(0), min(patternMillis[i],
                MAX_VIBRATE_PATTERN_DELAY_NSECS / 1000000LL)) * 1000000LL;
    }
    env->ReleasePrimitiveArrayCritical(patternObj, patternMillis, JNI_ABORT);

    im->getInputManager()->getReader()->vibrate(deviceId, pattern, patternSize, repeat, token);
}

static void nativeCancelVibrate(JNIEnv* env,
        jclass clazz, jint ptr, jint deviceId, jint token) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    im->getInputManager()->getReader()->cancelVibrate(deviceId, token);
}
-- 对于vibratorOn /vibratorOff,其实现位于com_android_server_VibratorService.cpp中 
static JNINativeMethod method_table[] = {
    { "vibratorExists", "()Z", (void*)vibratorExists },
    { "vibratorOn", "(J)V", (void*)vibratorOn },
    { "vibratorOff", "()V", (void*)vibratorOff }
};
      
static void vibratorOn(JNIEnv *env, jobject clazz, jlong timeout_ms)
{
    // ALOGI("vibratorOn\n");
    vibrator_on(timeout_ms);
}

static void vibratorOff(JNIEnv *env, jobject clazz)
{
    // ALOGI("vibratorOff\n");
    vibrator_off();
}

3.HAL层    
 vib rator_on/vibrator_off的定义位于头文件<hardware_legacy/vibrator.h>(\hardware\libhardware_legacy\include\hardware_legacy\)
/**
 * Turn on vibrator
 *
 * @param timeout_ms number of milliseconds to vibrate
 *
 * @return 0 if successful, -1 if error
 */
int vibrator_on(int timeout_ms);

/**
 * Turn off vibrator
 *
 * @return 0 if successful, -1 if error
 */
int vibrator_off();
HAL已经完成了实现。 
\hardware\libhardware_legacy\vibrator\vibrator.c
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <hardware_legacy/vibrator.h>
#include "qemu.h"

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#define THE_DEVICE "/sys/class/timed_output/vibrator/enable"

int vibrator_exists()
{
    int fd;

#ifdef QEMU_HARDWARE
    if (qemu_check()) {
        return 1;
    }
#endif

    fd = open(THE_DEVICE, O_RDWR);
    if(fd < 0)
        return 0;
    close(fd);
    return 1;
}

static int sendit(int timeout_ms)
{
    int nwr, ret, fd;
    char value[20];

#ifdef QEMU_HARDWARE
    if (qemu_check()) {
        return qemu_control_command( "vibrator:%d", timeout_ms );
    }
#endif

    fd = open(THE_DEVICE, O_RDWR);
    if(fd < 0)
        return errno;

    nwr = sprintf(value, "%d\n", timeout_ms);
    ret = write(fd, value, nwr);

    close(fd);

    return (ret == nwr) ? 0 : -1;
}

int vibrator_on(int timeout_ms)
{
    /* constant on, up to maximum allowed time */
    return sendit(timeout_ms);
}

int vibrator_off()
{
    return sendit(0);
}

    这里使用的是Android的timed output机制,通过操纵/sys/class/timed_output/vibrator/enable节点启动或者停止启动器。
4.内核层
先看看timed output的驱动代码,位于Linux_Kernel_3x\drivers\staging\android\下。

#ifndef _LINUX_TIMED_OUTPUT_H
#define _LINUX_TIMED_OUTPUT_H

struct timed_output_dev {
	const char	*name;

	/* enable the output and set the timer */
	void	(*enable)(struct timed_output_dev *sdev, int timeout);

	/* returns the current number of milliseconds remaining on the timer */
	int		(*get_time)(struct timed_output_dev *sdev);

	/* private data */
	struct device	*dev;
	int		index;
	int		state;
};

extern int timed_output_dev_register(struct timed_output_dev *dev);
extern void timed_output_dev_unregister(struct timed_output_dev *dev);

#endif

所有的timed output实现都需要定义在结构体timed_output_dev中。
Timed Output驱动程序框架将为每个设备在/sys/class/timed_output/目录中建立一个子目录,设备子目录中的enable文件就是设备的控制文件。读enable文件表示获得剩余时间,写这个文件表示根据时间振动。
这个具体情况具体实现,参考:
http://tech.it168.com/a2012/0201/1305/000001305847.shtmlhttp://tech.it168.com/a2012/0201/1305/000001305847.shtml

 

 

小强的幻音宝盒
粉丝 1
博文 3
码字总数 6547
作品 0
广州
程序员
私信 提问
加载中
请先登录后再评论。
浅入浅出Android(003):使用TextView类构造文本控件

基础: TextView是无法供编辑的。 当我们新建一个项目MyTextView时候,默认的布局(/res/layout/activity_main.xml)中已经有了一个TextView: <TextView 运行效果如下: 修改其文本内容...

樂天
2014/03/22
708
1
程序猿媛一:Android滑动翻页+区域点击事件

滑动翻页+区域点击事件 ViewPager+GrideView 声明:博文为原创,文章内容为,效果展示,思路阐述,及代码片段。文尾附注源码获取途径。 转载请保留原文出处“http://my.oschina.net/gluoyer...

花佟林雨月
2013/11/09
4.3K
1
Android3D应用与游戏开发框架--JQGL

JQGL 是一款针对Android设备上3D应用、游戏的开发框架。 核心功能是OpenGL-ES的使用框架,相对于大部分开发者而已,OpenGL是陌生的,没有专门研究无法进行相关的开发。 本框架针对于Android...

Jping
2013/02/21
1.6K
0
Android-PanesLibrary

Android-PanesLibrary 可以让你非常轻松的创建原生的带多个面板的平板布局的 Android 应用。包括一个滑动菜单和内容面板,在平板上菜单和多个面板一起显示。 下图是用该组件开发的一个应用:...

匿名
2013/03/28
6.4K
0
Android 设备管理工具--androrat

androrat 是一个对 Android 设备进行远程管理的工具。 功能包括: 获取通讯录信息 获取呼叫记录 获取短信和彩信 通过 GPS 获取定位 实时监控接收到的短信 监控手机的呼叫状态 拍照 获取来自麦...

匿名
2013/03/28
2.7W
11

没有更多内容

加载失败,请刷新页面

加载更多

大数据研发学习之路--Hadoop集群搭建

阅读编译文档 准备一个hadoop源码包,我选择的hadoop版本是:hadoop-2.7.7-src.tar.gz,在hadoop-2.7.7的源码 包的根目录下有一个文档叫做BUILDING.txt,这其中说明了编译hadoop所需要的一些...

DSJ-shitou
24分钟前
8
0
OSChina 周五乱弹 —— 特么是别的公司派来的特洛伊木马吧?

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 小小编辑推荐:《我会守在这里》- 毛不易 《我会守在这里》- 毛不易 手机党少年们想听歌,请使劲儿戳(这里) @FalconChen :股市连跪了五天,...

小小编辑
25分钟前
26
2
如何在find中排除目录。命令 - How to exclude a directory in find . command

问题: I'm trying to run a find command for all JavaScript files, but how do I exclude a specific directory? 我正在尝试为所有JavaScript文件运行find命令,但是如何排除特定目录? ......

法国红酒甜
今天
69
0
《Java8实战》笔记(02):通过行为参数传递代码

本文源码 应对不断变化的需求 通过筛选苹果阐述通过行为参数传递代码 初试牛刀:筛选绿苹果 public static List<Apple> filterGreenApples(List<Apple> inventory){List<Apple> result = ......

巨輪
今天
19
0
JeeSite 4 架构特点、安全方面、为什么好、工匠精神、不忘初心

1、底层架构 以 Spring Boot 2 为基础,Maven 多项目依赖,模块分项目,松耦合,方便模块升级、增减模块。 模块化的数据库自动升级程序,当模块升级代码需要更新数据库时,自动执行对应版本 ...

ThinkGem
昨天
13
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部