Vibrator vibrator = (Vibrator)getSysteService(VIBRATOR_SERVICE); vibrator.vibrate(50);
Vibrator Service 对应关键字 [路径:\frameworks\base\core\java\android\content\Context.java]
/** * Use with {@link #getSystemService} to retrieve a {@link * android.os.Vibrator} for interacting with the vibration hardware. * * @see #getSystemService * @see android.os.Vibrator */ public static final String VIBRATOR_SERVICE = "vibrator";
利用VIBRATOR_SERVICE关键字,在ContextImpl.getSystemService(name)中获取对应的服务。 说明:基本上各种服务的获取,都是在该处进行check并分取的。比如ACTIVITY_SERVICE、INPUT_METHOD_SERVICE等等。 [路径:\frameworks\base\core\java\android\app\ContextImpl.java]
... } else if (VIBRATOR_SERVICE.equals(name)) { return getVibrator(); } else if (STATUS_BAR_SERVICE.equals(name)) { ...
private Vibrator getVibrator() { synchronized (mSync) { if (mVibrator == null) { mVibrator = new Vibrator(); } } return mVibrator; }
此处先通过ServiceManager获取一个Vibrator Service对应的Binder代理对象,再将该Binder代理对象做为IVibratorService.Stub.asInterface()的参数,并返回Vibrator的统一接口. [路径:\frameworks\base\core\java\android\os\Vibrator.java]
public class Vibrator { private static final String TAG = "Vibrator"; IVibratorService mService; private final Binder mToken = new Binder(); /** @hide */ public Vibrator() { mService = IVibratorService.Stub.asInterface( ServiceManager.getService("vibrator")); } ... }
接下来分别看看ServiceManager.getService()和IVibratorService.Stub.asInterface
IVibratorService.Stub.asInterface在IVibratorService.java中定义,这个IVibratorService.java代码可以由IVibratorService.aidl通过aidl工具自动生成的。aidl是一种android内部跨进程通讯接口的描述语言,即这里定义了跨进程的通讯接口描述。其对应的aidl描述如下: [路径:\frameworks\base\core\java\android\os\IVibratorService.aidl]
interface IVibratorService { void vibrate(long milliseconds, IBinder token); void vibratePattern(in long[] pattern, int repeat, IBinder token); void cancelVibrate(IBinder token); }
这里提供了三个接口函数: void vibrate(long milliseconds, IBinder token); void vibratePattern(in long[] pattern, int repeat, IBinder token); void cancelVibrate(IBinder token); 这三个接口函数可以说就是Vibrator的全部控制概括。应用层通过上述三个接口函数,跨进程访问VibratorService。需要说明的是,接口参数必须与其service约定,保证顺序一致,保证service通过代理binder顺序读取。对于VibratorService的介绍,后面单独说,这里先讲解本进程的流程。 下面我们回退到Vibrator JAVA框架中如何来调用上述三个接口函数的;
在JAVA框架中,提供了如下几个函数,供应用层调用: //震动指定时间长度(单位ms),时间到后结束震动 public void vibrate(long milliseconds); //震动间隔和周期可控的震动方式 //pattern 数组,数组单数位置数字为等待时间,双数位置数字为震动时间 //repeat 震动周期控制,-1为震动一个周期;0为一直震动,>0为重复pattern数组对应位置,必须repeat < pattern.length public void vibrate(long[] pattern, int repeat); public void cancel(); 由代码可以看出,应用层调用震动接口函数,实际上还是由aidl接口API通过代理binder对象去调用其service处理,知道最后驱动硬件。
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); } }
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(); } }
public void cancel() { if (mService == null) { return; } try { mService.cancelVibrate(mToken); } catch (RemoteException e) { Log.w(TAG, "Failed to cancel vibration.", e); } }
首先从sCache缓存中查看是否有对应的Binder对象,有则返回,没有则调用getIServiceManager().getService(name)获取一个vibrator Binder对象,其中getIServiceManager()用于返回系统中单独的ServiceManager对应的Binder代理对象。 [路径:\frameworks\base\core\java\android\os\ServiceManager.java]
/**
* Returns a reference to a service with the given name.
*
* @param name the name of the service to get
* @return a reference to the service, or null
if the service doesn't exist
*/
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
[路径:\frameworks\base\services\java\com\android\server\VibratorService.java] [路径:\frameworks\base\services\java\com\android\server\SystemServer.java]
上面说到,在应用进程中通过getIServiceManager()获取系统中单独的ServiceManager对应的Binder,那如何获取的呢? 当Zygote启动并初始化system时,会启动SystemServer系统服务,在SystemServer启动后,会创建一个线程,并在该线程中加载相关的服务,比如VibratorService。如下:
public void run() { ... Slog.i(TAG, "Vibrator Service"); ServiceManager.addService("vibrator", new VibratorService(context)); ... }
通过name,即可获取到对应的服务: ServiceManager.getService("vibrator")
Android不能直接跨进程访问。在android中,Google创建了一个跨进程访问的方式---aidl方式,由aidl提供访问的接口函数。在应用层的访问方式,前面已经讲过,我们知道通过如下三个函数进行访问控制Vibrator: void vibrate(long milliseconds, IBinder token); void vibratePattern(in long[] pattern, int repeat, IBinder token); void cancelVibrate(IBinder token); 那这三个函数是如何工作的呢?参考 VibratorService服务.基本的算法流程都在VibratorService.java中,可以自行跟踪调试。
但不管是何种方式开启震动或关闭震动,VibratorService都是通过JNI调用机制,调用本地框架(C/C++代码)中的接口。 native static void vibratorOn(long milliseconds); native static void vibratorOff(); [路径:\frameworks\base\services\jni\com_android_server_VibratorService.cpp]
namespace android { static void vibratorOn(JNIEnv *env, jobject clazz, jlong timeout_ms) { // LOGI("vibratorOn\n"); vibrator_on(timeout_ms); } static void vibratorOff(JNIEnv *env, jobject clazz) { // LOGI("vibratorOff\n"); vibrator_off(); } static JNINativeMethod method_table[] = { { "vibratorOn", "(J)V", (void*)vibratorOn }, { "vibratorOff", "()V", (void*)vibratorOff } }; int register_android_server_VibratorService(JNIEnv *env) { return jniRegisterNativeMethods(env, "com/android/server/VibratorService", method_table, NELEM(method_table)); } };
其中对应的vibrator_on(timeout_ms)及vibratorOff()如下: [路径:\hardware\libhardware_legacy\vibrator\Vibrator.c]
#define THE_DEVICE "/sys/class/timed_output/vibrator/enable" 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); }
其中实现的核心内容为sendit()函数,它负责根据时间“震动”,通过读写sys文件系统的设备文件进行控制; 在android中,是基于Android内核定义Timed Output驱动程序框架来实现Vibrator的驱动程序。Timed Output的含义为定时输出,用于定时发出某个输出。 Timed Output驱动程序框架为每个设备在 /sys/class/timed_output/目录下建立一个子目录,设备子目录的enable文件就是设备的控制文件。读enable文件表示获取剩余的时间,写这个文件表示根据时间震动。 对于Vibrator设备,其实现的Timed Output驱动程序的名称应该为"vibrator"
其实我们可以通过直接读写该文件系统来控制vibrator: 1. 用USB线连接手机与PC,保证连接正常。 2. 打开cmd.exe,路径切换到\android-sdk-windows\tools\ 3. 敲入: adb shell,回车,进入shell控制模式 4. 敲入: echo "2000" > sys/class/timed_output/vibrator/enable,回车,设置手机震动20s 5. 敲入: cat sys/class/timed_output/vibrator/enable, 回车,显示震动剩余时间