Журнал LinuxFormat - перейти на главную

LXF156:Android:программирование

Материал из Linuxformat
Перейти к: навигация, поиск


Android

Ваши программные наработки не пропадут даром

Часть 2: При­клад­ные ин­тер­фей­сы яд­ра. Ан­д­рей Бо­ров­ский рас­ска­жет то, что вы хо­те­ли знать про Android, но боя­лись спро­сить.

Про­дол­жим ув­ле­ка­тель­ное пу­те­ше­ст­вие в мир про­грам­ми­ро­вания для Android. Но это не то про­грам­ми­ро­вание, про ко­то­рое пи­шут тол­стые книги. В этом ми­ре про­грам­мы, не за­мет­ные поль­зо­ва­те­лю, бе­рут под кон­троль управ­ление пи­танием сис­те­мы и пе­ре­хва­ты­ва­ют поль­зо­ва­тель­ский ввод.

Итак, разберемся, что же мы име­ем. Са­мый важ­ный ин­ст­ру­мен­та­рий для нас – NDK. При уста­но­вке но­вей­шей (на мо­мент на­пи­сания тек­ста) вер­сии NDK – r7 – где-то в недрах ва­шей фай­ло­вой сис­те­мы долж­на поя­вить­ся ди­рек­то­рия android-ndk-r7. Ее мы бу­дем на­зы­вать NDK_ROOT (вклю­чая пол­ный путь к ней).

В NDK_ROOT вы уви­ди­те мно­же­ст­во под­ди­рек­то­рий непо­нят­но­го на­зна­чения. Не от­чаи­вай­тесь: во всем этом лег­ко ра­зо­брать­ся, тем бо­лее что для на­ших ха­кер­ских це­лей по­на­до­бит­ся не весь NDK. Од­на из важней­ших для нас ди­рек­то­рий – NDK_ROOT/toolchains. Как вид­но по на­званию, она со­дер­жит раз­лич­ные вер­сии ин­ст­ру­мен­та­рия сбор­ки при­ло­жений для Android. Фак­ти­че­­ски это стан­дарт­ный ин­ст­ру­мен­та­рий сбор­ки при­ло­жений GNU/Linux. Ди­рек­то­рия NDK_ROOT/toolchains вклю­ча­ет две под­ди­рек­то­рии: x86-4.4.3, пред­на­зна­чен­ная для сбор­ки про­грамм Android для плат­фор­мы x-86, и arm-linux-androideabi-4.4.3, ори­ен­ти­ро­ва­нная на плат­фор­му ARM (для дру­гой вер­сии NDK, циф­ры, ес­те­ст­вен­но, мо­гут быть дру­ги­ми). Раз мы усло­ви­лись пи­сать про­грам­мы для ARM, пе­ре­хо­дим в эту ди­рек­то­рию. В ее под­ди­рек­то­ри­ях есть все необ­хо­ди­мое для сбор­ки и от­лад­ки про­грамм Linux на плат­фор­ме ARM. На­по­м­ню, что са­ми ин­ст­ру­мен­ты сбор­ки пред­на­зна­че­ны для за­пуска на ПК ар­хи­тек­ту­ры Intel, а ре­зуль­та­том сбор­ки ста­нут про­грам­мы, для вы­полнения ко­то­рых по­тре­бу­ет­ся про­цес­сор се­мей­ст­ва ARM (или его эму­ля­тор).

Вто­рая важ­ная ди­рек­то­рия – NDK_ROOT/platforms. Она со­дер­жит биб­лио­те­ки и за­го­ло­воч­ные фай­лы для API Android раз­лич­ных вер­сий (уровней). В ней вы най­де­те несколь­ко под­ди­рек­то­рий ви­да android-x, где x – но­мер уров­ня API. Ме­ж­ду уров­ня­ми API и вер­сия­ми ОС Android су­ще­ст­ву­ет чет­кое со­от­вет­ст­вие. Ни­же при­во­дит­ся пе­ре­чень уровней API для наи­бо­лее по­пу­ляр­ных на дан­ный мо­мент вер­сий Android (пол­ную таб­ли­цу вы мо­же­те най­ти на сай­те раз­ра­бот­чи­ков Android – developer.android.com).

Версия Андроид Уровень API
1.5 3
2.0 5
2.1.x 7
2.2.x 8
2.3–2.3.2 9
3.0.x 11
3.1.x 12
3.2 13
4.0–4.0.2 14

В ка­ж­дой ди­рек­то­рии android-x имеются под­ди­рек­то­рии arch-x86 и arch-arm, для двух це­ле­вых плат­фор­м. Ка­ж­дая ди­рек­то­рия со­дер­жит под­ди­рек­то­рии /usr/lib и /usr/include – то, че­го нам так не хва­та­ло для сбор­ки про­грамм под Android. Сто­ит от­ме­тить, что не все биб­лио­те­ки Android ме­ня­лись при пе­ре­хо­де от од­но­го уров­ня API к дру­го­му. В NDK-r6 фай­лы неко­то­рых биб­лио­тек пред­став­ля­ли со­бой сим­воль­ные ссыл­ки на фай­лы тех же биб­лио­тек бо­лее ранних уровней API. Сравнивая API раз­ных уровней, вы за­ме­ти­те, что с но­ме­ром уров­ня ко­ли­че­­ст­во биб­лио­тек рас­тет – и не толь­ко по­то­му, что в но­вых вер­си­ях Android по­яв­ля­ют­ся но­вые ком­понен­ты, но и по­то­му, что бо­лее но­вые вер­сии Android пре­достав­ля­ют бо­лее ши­ро­кий доступ на уровне Linux API к ком­понен­там сис­те­мы, вве­ден­ным ранее. Итак, желая, что­бы ва­ши про­грам­мы по­лу­чи­ли мак­си­маль­ный доступ к функ­ци­ям Android, экс­пе­ри­мен­ти­руй­те с но­вей­шей вер­си­ей ОС. Не­труд­но ви­деть так­же, что раз­ные уровни API об­ла­да­ют об­рат­ной со­вмес­ти­мо­стью. Рас­смот­рим наи­бо­лее ин­те­рес­ные воз­мож­но­сти, доступ­ные про­грам­мам Linux в раз­ных вер­си­ях Android.

  • Android 1.5 (уро­вень API 3) На этом уровне нам доступ­ны биб­лио­те­ка Bionic, биб­лио­те­ка вре­мени вы­полнения C++ в несколь­ко уре­зан­ном ва­ри­ан­те, ин­тер­фейс ра­бо­ты с по­то­ка­ми POSIX Threads, то­же слегка уре­зан­ный, и стан­дарт­ная биб­лио­те­ка ма­те­ма­ти­че­­ских функ­ций. Все пе­ре­чис­лен­ное лин­ку­ет­ся с ис­пол­няе­мым фай­лом ав­то­ма­ти­че­­ски; спе­ци­аль­ных команд для под­клю­чения биб­лио­тек ука­зы­вать не нуж­но.

Биб­лио­те­ка zlib (ра­бо­та со сжа­ты­ми дан­ны­ми) долж­на под­клю­чать­ся яв­но (с по­мо­щью клю­ча --lz), как и биб­лио­те­ка libdl, требуемая для ди­на­ми­че­­ской за­груз­ки раз­де­ляе­мых биб­лио­тек.

  • Android 2.0 -2.3 До­бав­ле­ны под­держ­ка OpenGL ES 2.0 (биб­лио­те­ка libGLESv2.so, уро­вень API 5), Android bitmap API (уро­вень API 8), под­держ­ка EGL (libEGL.so, уро­вень API 9) и под­держ­ка OpenSL ES (libOpenSLES.so, уро­вень API 9)

API 9 во­об­ще мож­но на­звать про­ры­вом в об­лас­ти про­грам­ми­ро­вания для Android: имен­но в нем поя­вил­ся ин­тер­фейс про­грам­ми­ро­вания Android native application API для про­грамм Linux (биб­лио­те­ка libandroid.so). Эта биб­лио­те­ка по­зво­ля­ет при­ло­жению Linux взаи­мо­дей­ст­во­вать с сис­те­мой так же, как это де­ла­ет при­ло­жение, на­пи­сан­ное на Java (вклю­чая гра­фи­че­­ский ввод-вы­вод).

  • Android 4 (уро­вень API 14) До­бав­ле­на под­держ­ка ин­тер­фей­са OpenMAX AL (биб­лио­те­ка libOpenMAXAL.so), по­зво­ляю­ще­го ра­бо­тать с ап­па­рат­но-уско­рен­ны­ми по­то­ка­ми муль­ти­ме­диа-дан­ных.

Те­перь у нас есть все необ­хо­ди­мые ин­ст­ру­мен­ты для на­пи­сания про­грамм Linux под Android. Ос­та­лось толь­ко ов­ла­деть несколь­ки­ми трю­ка­ми, что­бы нау­чить­ся пра­виль­но (или непра­виль­но – это с ка­кой сто­ро­ны взглянуть) ими поль­зо­вать­ся.

Наш пер­вый Make-файл

Для сбор­ки про­грамм Linux под Android нам по­на­до­бит­ся спе­ци­аль­ный Make-файл. Make-файл, пред­на­зна­чен­ный для сбор­ки да­же са­мой про­стой про­грам­мы Linux-Android, вы­гля­дит до­воль­но слож­но и ис­поль­зу­ет прие­мы, ред­кие в обыч­ном про­грам­ми­ро­вании для Linux. Но этот Make-файл мож­но применить как шаб­ло­н для соз­дания фай­лов, управ­ляю­щих сбор­кой бо­лее слож­ных про­грамм, при­чем менять шаб­лон при­дет­ся не так уж сильно.

Ос­воение Android мы начнем с про­стей­шей про­грам­мы test:

#include <stdio.h>

#include <stdlib.h>

int main(int argc, char **argv)

{

printf(“Hello, world!\n”);

exit(0);

return 0;

}

Она от­ли­ча­ет­ся от тра­ди­ци­он­но­й для Linux про­грам­мы Hello World. Вы­ве­дя со­об­щение, мы вы­зы­ва­ем функ­цию exit() – она за­вер­ша­ет ра­бо­ту про­грам­мы (опе­ра­тор return ниче­го не де­ла­ет и со­хранен толь­ко ради под­дер­жки стан­дарт­ного син­так­сиса). Функ­ция exit() нужна нам по­то­му, что в на­шем рас­по­ря­жении нет биб­лио­те­ки GNU glibc, ко­то­рая бы об­слу­жи­ва­ла про­грам­му. Без явного вы­зо­ва exit() про­грам­ма test бу­дет вы­пол­нять­ся и по­сле вы­хо­да из main(), по­ка не вы­зо­вет ка­кое-либо ис­клю­чение. Да­лее мы рас­смот­рим це­лых два спо­со­ба пре­доста­вить функ­ции main() удоб­ст­ва биб­лио­те­ки libc без са­мой этой биб­лио­те­ки.

Рас­смот­рим Make-файл для про­грам­мы test – он станет осно­вой ана­ло­гич­ных фай­лов для всех осталь­ных наших про­грамм.

APP := test

SRC:=test.c

SDK_ROOT:=/home/andrei/android-sdk-linux_x86

NDK_ROOT:=/home/andrei/android-ndk-r7

NDK_API_LEVEL := android-3

NDK_HOST:=linux-x86

PREBUILD:=$(NDK_ROOT)/toolchains/arm-linux-androideabi-4.4.3/prebuilt/$(NDK_HOST)

BIN := $(PREBUILD)/bin

GDB_CLIENT := $(BIN)/arm-linux-androideabi-gdb

FS_ROOT := $(NDK_ROOT)/platforms/$(NDK_API_LEVEL)/arch-arm

INSTALL_DIR := /storage

DEBUG = -g

CPP := $(BIN)/arm-linux-androideabi-g++

CC := $(BIN)/arm-linux-androideabi-gcc

CFLAGS := $(DEBUG) -I$(FS_ROOT)/usr/include

LDFLAGS := -Wl,--entry=main,-rpath-link=$(FS_ROOT)/usr/lib,-dynamic-linker=/system/bin/linker -L$(FS_ROOT)/usr/lib

LDFLAGS += -nostdlib -lc

all: $(APP)

OBJS += $(APP).o

$(APP): $(OBJS)

$(CPP) $(LDFLAGS) -o $@ $^

$(APP).o: $(SRC)

$(CC) $(SRC) -c $(INCLUDE) $(CFLAGS) -o $@

install: $(APP)

$(SDK_ROOT)/platform-tools/adb push $(APP) $(INSTALL_DIR)/$(APP)

$(SDK_ROOT)/platform-tools/adb shell chmod 777 $(INSTALL_DIR)/$(APP)

shell:

$(SDK_ROOT)/platform-tools/adb shell

run:

$(SDK_ROOT)/platform-tools/adb shell $(INSTALL_DIR)/$(APP)

debug:

$(GDB_CLIENT) $(APP)

clean:

@rm -f *.o $(APP)

Смысл пе­ре­мен­ных, объ­яв­лен­ных в на­ча­ле фай­ла, дол­жен быть ясен. По­сле всех манипу­ля­ций пе­ре­мен­ная BIN со­дер­жит пол­ное имя ди­рек­то­рии, где хранит­ся па­кет GCC для ARM и его дру­зья. Пе­ре­мен­ная FS_ROOT ука­зы­ва­ет сис­те­ме сбор­ки, где ис­кать ди­рек­то­рии include и lib для под­клю­чения до­бавочных биб­лио­тек. При за­пи­си зна­чения в эту пе­ре­мен­ную ис­поль­зу­ет­ся пе­ре­мен­ная API_LEVEL, задающая уро­вень API (то есть под­ди­рек­то­рию ди­рек­то­рии NDK_ROOT/platforms) для дан­ной сбор­ки. На­шей пер­вой про­грам­ме мно­го не на­до, и мы ис­поль­зу­ем API уров­ня 3. Пе­ре­мен­ная INSTALL_DIR хранит ди­рек­то­рию фай­ло­вой сис­те­мы це­ле­во­го уст­рой­ст­ва, куда долж­но быть ско­пи­ро­ва­но со­б­ран­ное при­ло­жение.

Да­лее объ­яв­ляются пе­ре­мен­ные, со­дер­жащие клю­чи для ком­пи­ля­то­ра, ком­по­нов­щи­ка и от­лад­чи­ка. Ес­ли вы зна­ко­мы с ин­ст­ру­мен­та­ри­ем сбор­ки GNU, зна­чения пе­ре­мен­ных DEBUG, CC, CPP и CFLAGS не долж­ны вы­зы­вать у вас во­про­сов. Ин­те­рес­ное на­чи­на­ет­ся с пе­ре­мен­ной LDFLAGS. Ключ --entry ред­ко ис­поль­зу­ет­ся при сбор­ке обыч­ных про­грамм Linux; он по­зво­ля­ет ука­зать имя функ­ции, ко­то­рая бу­дет точ­кой вхо­да в про­грам­му. При ис­поль­зо­вании стан­дарт­ной glibc точ­кой вхо­да яв­ля­ет­ся функ­ция _start(), пре­достав­ляе­мая glibc, и ком­по­нов­щик зна­ет об этом. У про­грам­мы test нет функ­ции _start(), и в ка­че­­ст­ве точ­ки вхо­да мы ука­зы­ва­ем функ­цию main().

Со­че­тание клю­чей --rpath-link и --dynamic-linker по­зво­ля­ет обой­ти про­бле­му, свя­зан­ную с тем, что фай­ло­вая сис­те­ма це­ле­во­го уст­рой­ст­ва вы­гля­дит не так, как фай­ло­вая сис­те­ма ПК, на ко­то­ром мы со­би­ра­ем про­грам­му. При сбор­ке на ПК про­грам­ма-ком­по­нов­щик долж­на ис­кать биб­лио­те­ки не там, где под­ска­зы­ва­ет ей сис­те­ма (сис­те­ма пред­ло­жит стан­дарт­ные биб­лио­те­ки Linux для ПК, ко­то­рые нам не под­хо­дят), а там, где мы ука­жем. Так как на це­ле­вом уст­рой­ст­ве эти биб­лио­те­ки рас­по­ло­же­ны со­всем в дру­гих мес­тах, мы ис­поль­зу­ем ди­на­ми­че­­ский за­груз­чик биб­лио­тек (/system/bin/linker – путь к ком­по­нов­щи­ку в фай­ло­вой сис­те­ме це­ле­во­го уст­рой­ст­ва), ко­то­рый вы­полнит «ин­тел­лек­ту­аль­ное» свя­зы­вание про­грам­мы с нуж­ны­ми ей биб­лио­те­ка­ми.

Со­че­тание клю­чей --no-stdlib и --lc выглядит из­де­ва­тель­ским: сна­ча­ла мы за­пре­ща­ем свя­зы­вать про­грам­му со стан­дарт­ной биб­лио­те­кой C, а за­тем вы­пол­ня­ем-таки свя­зы­вание. Объ­яс­ня­ет­ся это со­че­тание тем, что стан­дарт­ная биб­лио­те­ка C (Bionic), с ко­то­рой мы свя­зы­ва­ем про­грам­му, не яв­ля­ет­ся стан­дарт­ной glibc.

Ме­ж­ду про­чим, кон­цеп­ция, реа­ли­зо­ван­ная в пред­став­лен­ном Make-фай­ле, го­дит­ся не толь­ко для Android, но и для лю­бой Linux-по­доб­ной сис­те­мы, у ко­то­рой есть ди­на­ми­че­­ский за­груз­чик биб­ли­тек. Все, что нуж­но – это об­за­вес­тись раз­де­ляе­мы­ми биб­лио­те­ка­ми и за­го­ло­воч­ны­ми фай­ла­ми для со­от­вет­ст­вую­щей сис­те­мы.

Об­ра­ти­те внимание на объ­яв­лен­ную в Make-фай­ле цель install. Для уста­нов­ки про­грам­мы на це­ле­вое уст­рой­ст­во ис­поль­зу­ет­ся опи­сан­ная в пре­ды­ду­щей час­ти ути­ли­та adb из SDK. Как бы­ло ска­за­но в пре­ды­ду­щей час­ти, для вы­полнения этих команд вам мо­гут по­на­до­бить­ся пра­ва root, ко­то­рые по умол­чанию пре­достав­ля­ет толь­ко эму­ля­тор уст­ройств Android, вхо­дя­щий в со­став SDK. Ос­та­лось со­брать про­грам­му и убе­дить­ся в том, что она ра­бо­та­ет.

Что­бы преодолеть неудобства и­з-за от­сут­ст­ви­я в про­грам­ме стан­дарт­ной функ­ции _start(), на­пи­шем свой ва­ри­ант этой функ­ции (файл crt0.s):

crt0.s:

.text

.global _start

_start:

[[Файл:Файл:Pic1_opt.jpeg |right |400px| Файл crtbegin_dynamic.o, ди­зас­семб­ли­ро­ван­ный с по­мо­щью про­грам­мы objdump.]]  

mov	r0, sp

mov	r1, #0

add	r2, pc, #4

add	r3, pc, #4;

b	__libc_init

b	main

.word	__preinit_array_start

.word	__init_array_start

.word	__fini_array_start

.word	__ctors_start

.word	0

.word	0

...

Здесь мы ог­раничим­ся фраг­мен­том фай­ла (пол­ный ва­ри­ант – на дис­ке). Не вда­ва­ясь в детали сверх ме­ры, от­ме­тим, что функ­ция _start() просто вы­зы­ва­ет функ­цию __libc_init(), а за­тем функ­цию main(). __libc_init() под­го­тав­ли­ва­ет сре­ду ок­ру­жения для функ­ции main(). По­ми­мо про­че­го, в ре­зуль­та­те вы­зо­ва __libc_init() функ­ция main() по­лу­ча­ет кор­рект­ные зна­чения па­ра­мет­ров argc и argv (__libc_init() за­пи­сы­ва­ет их в ре­ги­ст­ры r0 и r1) и мо­жет за­вер­шать­ся опе­ра­то­ром return без вы­зо­ва функ­ции exit(). Make-файл для сбор­ки про­грам­мы при­дет­ся слегка по­менять:

SRC:=test.c crt0.s

OBJS := $(APP).o crt0.o

LDFLAGS := -Wl,--entry=_start,-rpath-link=$(FS_ROOT)/usr/lib,-dynamic-linker=/system/bin/linker -L$(FS_ROOT)/usr/lib

Ду­маю, что мо­ди­фи­ка­ции в по­яснениях особо не ну­ж­да­ют­ся.

На­ша функ­ция _start по­лез­на ско­рее в це­лях показа, что про­ис­хо­дит в стан­дарт­ной про­грам­ме ARM Linux. В ка­та­ло­ге lib вы­бран­ной ва­ми плат­фор­мы NDK вы най­де­те файл crtbegin_dynamic.o, со­дер­жащий, по су­ти, объ­ект­ный код той же функ­ции _start, лю­без­но ском­пи­ли­ро­ван­ной для нас раз­ра­бот­чи­ка­ми Android. Что­бы восполь­зо­вать­ся этой функ­ци­ей вме­сто при­ве­ден­ной вы­ше, доста­точ­но мо­ди­фи­ци­ро­вать зна­чение пе­ре­мен­ной OBJS:

OBJS := $(APP).o $(FS_ROOT)/usr/lib/crtbegin_dynamic.o

Бло­ки­ров­ки от­клю­че­ния пи­та­ния

Те­перь ис­сле­дуем Android с точ­ки зрения про­грам­ми­ста Linux. Од­ной из спе­ци­фи­че­­ских черт яд­ра Android яв­ля­ют­ся бло­ки­ров­ки от­клю­чения пи­тания [wakelocks]. Как и боль­шин­ст­во дру­гих мо­биль­ных уст­ройств, уст­рой­ст­ва Android ав­то­ма­ти­че­­ски пе­ре­хо­дят в один из ре­жи­мов эко­но­мии энер­гии, ес­ли какое-то время не про­ис­хо­дит со­бы­тий, тре­бую­щих ак­тив­ной ра­бо­ты уст­рой­ст­ва. Оче­вид­но, не все про­грам­мы устроит та­кой ре­жим ра­бо­ты: не­ко­то­рым важ­на га­ран­тия, что сис­те­ма не сразу уй­дет в спяч­ку. Этой це­ли и слу­жат бло­ки­ров­ки от­клю­чения пи­тания.

Рас­смот­рим при­мер про­грам­мы, ко­то­рая бло­ки­ру­ет от­клю­чение пи­тания на 10 ми­нут (файл wakelock.c)

int main(int argc, char ** argv)

{

[[Файл:Pic2_opt.jpeg |left |400px| На­ша бло­ки­ров­ка от­клю­че­ния пи­та­ния в ок­не вир­ту­аль­но­го тер­ми­на­ла Android.]]  

int lock_fd;

int unlock_fd;

char * name = “native_test_lock”;

lock_fd = open(“/sys/power/wake_lock”, O_WRONLY);

if (lock_fd < 0) {

printf (“locking failed\n”);

return 1;

}

write(lock_fd, name, strlen(name));

close(lock_fd);

printf(“locking system in a wake state for 10 minutes\n”);

sleep(600);

unlock_fd = open(“/sys/power/wake_unlock”, O_WRONLY);

if (unlock_fd < 0) {

printf (“unlocking failed\n”);

return 1;

}

write(unlock_fd, name, strlen(name));

close(unlock_fd);

printf(“unlocking system\n”);

return 0;

}
<pre>
Как ви­дим, ин­тер­фейс бло­ки­ро­вок от­клю­чения пи­тания весь­ма прост и даже не тре­бу­ет спе­ци­аль­но­го API. Для вклю­чения бло­ки­ров­ки нуж­но лишь за­пи­сать уникаль­ную стро­ку (имя бло­ки­ров­ки) в файл /sys/power/wake_lock; а для от­клю­чения – за­пи­сать ту же стро­ку в файл /sys/power/wake_unlock. Убе­дить­ся в том, что бло­ки­ров­ка успеш­но соз­да­на, мож­но с по­мо­щью ко­ман­ды

cat /proc/wakelocks

==Пе­ре­хват со­бы­тий кла­виа­ту­ры==

Ну, а те­перь на­пи­шем на­стоя­щую ха­кер­скую про­грам­му (в луч­шем, или, на­обо­рот, в худ­шем смыс­ле это­го сло­ва): про­грам­му-кей­лог­гер, неза­мет­но для осталь­ной сис­те­мы фик­си­рующую на­жа­тия на кла­виа­ту­ре Android. Про­грам­ма пригодится для об­ще­го понимания ра­бо­ты ввода в ОС Android, от­лад­ки и тес­ти­ро­вания вво­да и дру­гих хо­ро­ших ве­щей. На­де­юсь, вы не думаете применять этот код для пло­хих ве­щей? О нет, я вас та­ко­му не учил.

В про­грам­ме пе­ре­хва­та дан­ных, по­сту­па­ющих с кла­виа­ту­ры Android, мы за­дей­ст­ву­ем ме­ханизм т. н. input events [со­бы­тий вво­да]. В лю­бой сис­те­ме Linux, вклю­чая Android, е­сть ди­рек­то­рия /dev/input/ – она со­дер­жит фай­лы, со­от­вет­ст­вую­щие раз­лич­ным уст­рой­ст­вам вво­да. Счи­ты­вая дан­ные из этих фай­лов, вы по­лу­чи­те ин­фор­ма­цию о со­бы­ти­ях вво­да со­от­вет­ст­вую­ще­го уст­рой­ст­ва. Ин­фор­ма­ция о со­бы­тии пе­ре­да­ет­ся в струк­ту­ре тако­го ви­да:
<pre>
typedef struct {

struct timeval time;

unsigned short type;

unsigned short code;

unsigned int value;

} input_event;

По­ле timeval со­дер­жит ин­фор­ма­цию о вре­мени на­сту­п­ления со­бы­тия. По­ле type со­дер­жит дан­ные о ти­пе со­бы­тия. Для кла­виа­ту­ры Android воз­мож­ны два ти­па со­бы­тий: со­бы­тие, свя­зан­ное с кла­ви­шей (фи­зи­че­­ской или вир­ту­аль­ной) – код 1, и со­бы­тие, по­вто­ряющее пре­ды­ду­ще­е – код 0x14. По­ле code со­дер­жит скан-код кла­ви­ши. По­ле value со­дер­жит со­стояние кла­ви­ши, вы­звав­шее со­бы­тие (1 – кла­ви­ша на­жа­та, 0 – кла­ви­ша от­пу­ще­на).

В сис­те­ме Android со­бы­тия кла­виа­ту­ры мож­но добыть из фай­ла /dev/input/event0. Ин­фор­ма­ция о со­бы­тии счи­ты­ва­ет­ся из ­не­го в фор­ма­те описан­ной вы­ше струк­ту­ры input_event. Для ра­бо­ты с со­бы­тия­ми вво­да мы на­пи­шем API, со­стоя­щий из трех про­стых функ­ций (файл keys.c):

int keys_open(int blocking)

{

[[Файл:Файл:Pic3_opt.jpeg |left |400p|Ин­фор­ма­ция о на­жа­тых кла­ви­шах на эк­ра­не от­ла­доч­но­го тер­ми­на­ла.]]  

int block = blocking == KEYS_OPEN_NONBLOCKING ? O_NONBLOCK : 0;

int input = open(“/dev/input/event0”, O_RDONLY|block);

return input;

}

int keys_get(int handle, input_event *ie)

{

ie->code = 0;

read(handle, ie, sizeof(input_event));

if (ie->code) {

return 1;

}

return 0;

}

void keys_close(int handle)

{

close(handle);

}

Файл keys.c и заголовочный файл keys.h вы найдете на дис­ке. Са­ма про­грам­ма-кейлоггер (файл input.c), вы­гля­дит так:

int main(int argc, char ** argv)

{

input_event event;

int handle;

handle = keys_open(KEYS_OPEN_NONBLOCKING);

while(1) {

if (keys_get(handle, &event))

printf(“type: %i; code: %i; value: %i\n”, event.type, event.code, event.value);

}

return 0;

}

Дан­ные о на­жа­тых и от­пу­щен­ных кла­ви­шах рас­пе­ча­ты­ва­ют­ся на эк­ран кон­со­ли в бес­конеч­ном цик­ле.

В за­ви­си­мо­сти от зна­чения па­ра­мет­ра, пе­ре­дан­но­го функ­ции keys_open(), функ­ция keys_get() мо­жет ра­бо­тать в бло­ки­рую­щем ли­бо небло­ки­рую­щем ре­жи­ме. Мы вы­зы­ва­ем функ­цию в небло­ки­рую­щем ре­жи­ме, в ко­то­ром она воз­вра­ща­ет управ­ление сра­зу же, неза­ви­си­мо от то­го, поя­ви­лись ли но­вые со­бы­тия вво­да или нет. По­лу­чив ин­фор­ма­цию об оче­ред­ном со­бы­тии вво­да, функ­ция keys_get() воз­вра­ща­ет зна­чение 1, в про­тив­ном слу­чае – 0.

Как уже от­ме­ча­лось, про­грам­ма не ме­ша­ет ра­бо­те сис­те­мы, и поль­зо­ва­тель уст­рой­ст­ва, ско­рее все­го, во­об­ще не за­ме­тит ее при­сут­ст­вия. Для за­пуска про­грам­мы не тре­бу­ет­ся прав су­пер­поль­зо­ва­те­ля, хо­тя та­кие пра­ва, ско­рее все­го, по­на­до­бят­ся для ее уста­нов­ки. Конеч­но, на­стоя­щий кей­лог­гер не стал бы вы­во­дить ко­ды на­жа­тых кла­виш на кон­соль, а от­сы­лал бы их по се­ти... Впро­чем, за­будь­те, что я это го­во­рил.

Воз­мож­но, вы за­хо­ти­те ис­сле­до­вать, за ка­кие уст­рой­ст­ва от­ве­ча­ют дру­гие фай­лы в ди­рек­то­рии /dev/input/. Это мож­но сде­лать с по­мо­щью про­стой про­грам­мы, по­ка­зан­ной ниже.

int main (int agrc, char ** argv)

{

int fd = -1;

char name[256]= “Unknown”;

if ((fd = open(argv[1], O_RDONLY)) < 0) {

perror(“evdev open”);

exit(1);

}

if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) {

perror(“evdev ioctl”);

}

printf(“The device on %s says its name is %s\n”,

argv[1], name);

close(fd);

return 0;

}

Вы най­де­те этот код в фай­ле devices.c. За­пус­кать ском­пи­ли­ро­ван­ную про­грам­му на­до так:

devices /dev/input/event1

В ре­зуль­та­те бу­дет рас­пе­ча­та­но по­нят­ное для че­ло­ве­ка на­звание уст­ройств вво­да, ко­то­ро­му со­от­вет­ст­ву­ет ука­зан­ный файл.

Персональные инструменты
купить
подписаться
Яндекс.Метрика