闲着没事下载6.0的源码看下指纹模块是怎么写,记录一下学习过程。
本篇主要是介绍如何用虚拟机进行指纹相关的操作。以及修改HAL层,提供未实现的功能。
编译成功后,使用emulator打开虚拟机。emulator在这个目录:
prebuilts/android-emulator/linux-x86_64/emulator
emulator -selinux disabled -sysdir out/target/product/generic/
注意把selinux关了,不然要去配置selinux,否则各种权限问题。
进入设置->安全->指纹。
ok,一直next到指纹录入的界面。输入:
adb emu finger touch 1
模拟手去点击传感器。输入一次就录入成功了。此时按power键进入待机再按亮屏,就可以看见keyguard上有一个指纹标志了。再输入adb emu finger touch 1就可以解锁了。
这个命令找了好久,从hal层看到是通过qemu通信的,一直在代码里搜,结果没搜到。后来才发现下载的源码没有qemu,去github里下了一份就能搜到相关的了。最后是google到的。这里有一篇介绍6.0API的也有提及。
http://blog.csdn.net/yuzepeng997/article/details/50477091
1.Install Android SDK Tools Revision 24.3, if you have not done so. 2.Enroll a new fingerprint in the emulator by going to Settings > Security > Fingerprint, then follow the enrollment instructions. 3.Use an emulator to emulate fingerprint touch events with the following command. Use the same command to emulate fingerprint touch events on the lockscreen or in your app. adb -e emu finger touch
On Windows, you may have to run telnet 127.0.0.1 followed by finger touch .
也可以通过telnet去执行命令。还有help可以看,不像emu命令,输入错了也没反应。
telnet 127.0.0.1 5554
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Android Console: type 'help' for a list of commands
OK
KO: unknown command, try 'help'
help
Android console command help:
help|h|? print a list of commands
event simulate hardware events
geo Geo-location commands
gsm GSM related commands
cdma CDMA related commands
kill kill the emulator instance
network manage network settings
power power related commands
quit|exit quit control session
redir manage port redirections
sms SMS related commands
avd control virtual device execution
window manage emulator window
qemu QEMU-specific commands
sensor manage emulator sensors
finger manage emulator finger print
help finger
allows you to touch the emulator finger print sensor
available sub-commands:
finger touch touch finger print sensor with
finger remove remove finger from the fingerprint sensor
OK
finger touch 1
通过上面两步,有指纹数据后,就可以在APP里去使用FingerprintManager调用指纹验证的方法了。不过我是想研究一下录入时候的过程。原生的只能录入指纹,不能删除指纹。而且录入的时候一下就成功了,看不到录入时候的ui变化。主要是hal层没实现这些功能,下面要做的就是去把未实现的方法加上去。
虚拟器的指纹hal层代码在
device/generic/goldfish/fingerprint
fingerprint_remove函数是空的,研究了一下录入是把指纹数据写到/data/fingerprint.txt里,所以删除的时候把文件的对应id的指纹记录删除就好了,用了比较笨的方法,把其他id的指纹记录写到临时文件去,再把临时文件重命名为fingerprint.txt。好久没碰过c了,写的过程各种坑。
记得包含stdio头文件,为了方便定义了一个临时文件名的宏
#define FINGERPRINT_TXT_FILENAME_TMP "/data/fingerprint.txt_tmp"
#include
static int fingerprint_remove(struct fingerprint_device *_dev,
uint32_t gid, uint32_t fid) {
// TODO: implement enroll and remove, and set dev->authenticator_id = 0 when no FPs enrolled
emu_fingerprint_hal_device_t *dev = (emu_fingerprint_hal_device_t *)_dev;
FILE* fp_tmp = fopen(FINGERPRINT_TXT_FILENAME_TMP, "a");
FILE* src = fopen(FINGERPRINT_TXT_FILENAME, "r");
int i=0;
int find=0;
for(i=0; ilistener.num_fingers_enrolled; i++){
uint32_t fingerid = 0;
uint64_t secureid = 0;
uint64_t authenid = 0;
if(fscanf(src, "%d %" SCNu64 " %" SCNu64, &fingerid, &secureid, &authenid) == 3) {
if(fingerid != fid){
ALOGD("fingerprint_remove: read ,id=%d", fingerid);
save_fingerid(fp_tmp, fingerid, secureid, authenid);
}else{
ALOGD("fingerprint_remove: read, find it,id=%d", fingerid);
find=1;
}
}
}
fclose(src);
fclose(fp_tmp);
ALOGD("fingerprint_remove: write in tmp done");
if(find==1){
ALOGD("fingerprint_remove: update listener status");
FILE* fp_stored = fopen(FINGERPRINT_TXT_FILENAME_TMP, "r");
if (fp_stored) {
for(i=0; ilistener.num_fingers_enrolled; i++){
dev->listener.all_fingerids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = 0;
dev->listener.all_secureids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = 0;
dev->listener.all_authenids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = 0;
}
dev->listener.num_fingers_enrolled = 0;
while (1) {
int fingerid = 0;
uint64_t secureid = 0;
uint64_t authenid = 0;
if(fscanf(fp_stored, "%d %" SCNu64 " %" SCNu64, &fingerid, &secureid, &authenid) == 3) {
dev->listener.all_fingerids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = fingerid;
dev->listener.all_secureids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = secureid;
dev->listener.all_authenids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = authenid;
++ dev->listener.num_fingers_enrolled;
} else {
break;
}
}
fclose(fp_stored);
}
ALOGD("fingerprint_remove: update file");
fclose(dev->listener.fp_write);
rename(FINGERPRINT_TXT_FILENAME_TMP, FINGERPRINT_TXT_FILENAME);
dev->listener.fp_write = fopen(FINGERPRINT_TXT_FILENAME, "a");
send_removed_notice(dev, gid, fid);
return 0;
}
remove(FINGERPRINT_TXT_FILENAME_TMP);
return FINGERPRINT_ERROR;
}
删除成功后,发消息通知上层。
static void send_removed_notice(emu_fingerprint_hal_device_t* dev, uint32_t gid, uint32_t fid)
{
ALOGD("fingerprint_remove:send_removed_notice:%d", fid);
fingerprint_msg_t message = {0};
pthread_mutex_lock(&dev->listener.mutex);
message.type = FINGERPRINT_TEMPLATE_REMOVED;
message.data.removed.finger.fid = fid;
message.data.removed.finger.gid = gid;
pthread_mutex_unlock(&dev->listener.mutex);
pthread_mutex_lock(&dev->lock);
dev->device.notify(&message);
pthread_mutex_unlock(&dev->lock);
}
添加完编译后,懒得编译system.img,直接把fingerprint.goldfish.so push进去,然后把fingerprintd直接kill掉就能测试了。现在可以正常删除指纹,感觉很爽,不用为了在录入5个之后能再重新录入,手动去删掉/data/fingerprint.txt和/data/system/users/0/settings_fingerprint.xml了。/data/system/users/0/settings_fingerprint.xml保存的是上层的指纹记录。
下一个目标是能看到录入过程。
在worker_thread_t里添加一个变量samples_remaining保存录入的进度。
typedef struct worker_thread_t {
...
uint32_t samples_remaining;
} worker_thread_t;
在setListenerState函数初始化为10,这个随意,和app里对应就行,也可以提供个接口给app调用,但改动文件太多,我就不写了。
static void setListenerState(emu_fingerprint_hal_device_t* dev, worker_state_t state) {
pthread_mutex_lock(&dev->listener.mutex);
dev->listener.state = state;
dev->listener.samples_remaining = 10;
pthread_mutex_unlock(&dev->listener.mutex);
}
改写一下监听函数里录入时的逻辑,每次收到有效手指的时候,samples_remaining递减,在赋给message.data.enroll.samples_remaining传递给上层。samples_remaining等于0就是录入完成了,然后把指纹保存下来。
static void listener_send_notice(emu_fingerprint_hal_device_t* dev)
{
...
if (dev->listener.state == STATE_ENROLL) {
message.type = FINGERPRINT_TEMPLATE_ENROLLING;
message.data.enroll.finger.fid = dev->listener.fingerid;
if (!finger_already_enrolled(dev)){
--dev->listener.samples_remaining;
int32_t remaining = dev->listener.samples_remaining;
ALOGD("remaining %d", remaining);
message.data.enroll.samples_remaining = remaining;
if(remaining==0) {
dev->authenticator_id = get_64bit_rand();
dev->listener.all_fingerids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = dev->listener.fingerid;
dev->listener.all_secureids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = dev->secure_user_id;
dev->listener.all_authenids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = dev->authenticator_id;
++ dev->listener.num_fingers_enrolled;
save_fingerid(dev->listener.fp_write, dev->listener.fingerid, dev->secure_user_id, dev->authenticator_id);
}
is_valid_finger = true;
}
} else {
....
}
接着去APP里改一下。
packages/apps/Settings/src/com/android/settings/fingerprint/FingerprintEnrollSidecar.java
private final static int STEP = 9;
private int mEnrollmentSteps = STEP;
private int mEnrollmentRemaining = STEP+1;
private void startEnrollment() {
mHandler.removeCallbacks(mTimeoutRunnable);
mEnrollmentSteps = STEP;
mEnrollmentCancel = new CancellationSignal();
getActivity().getSystemService(FingerprintManager.class).enroll(mToken, mEnrollmentCancel,
0 /* flags */, mEnrollmentCallback);
mEnrolling = true;
}
private void cancelEnrollment() {
mHandler.removeCallbacks(mTimeoutRunnable);
if (mEnrolling) {
mEnrollmentCancel.cancel();
mEnrolling = false;
mEnrollmentSteps = STEP;
}
}
push完,重启fingerprintd,每次输入指令。就可以看到录入进度条会变了。中间的指纹动画挺酷炫的,用VectorDrawable和AnimatedVectorDrawable实现的。
最后还会有一个问题,每次退出指纹列表界面,fingerprintd都会崩溃,看了log发现是没找到fingerprint_post_enroll这个函数。去hal层声明一个空实现就行了。
static int fingerprint_post_enroll(struct fingerprint_device *dev){
ALOGD("fingerprint_post_enroll");
return 0;
}
在fingerprint_open里给dev初始化一下。
static int fingerprint_open(const hw_module_t* module, const char __unused *id,
hw_device_t** device)
{
...
dev->device.pre_enroll = fingerprint_pre_enroll;
dev->device.enroll = fingerprint_enroll;
dev->device.post_enroll = fingerprint_post_enroll;
...
}
至此,已经比较正常了,但在指纹列表界面发现了一个问题。每次发命令,对应指纹id的item项都会高亮一下,但是连续几次之后就不行了,发现是指纹验证被取消了,但是只有在onPause()方法里才会取消,界面又没变过,很奇怪,有时间再debug一下。