さて、順番に始めましょう。
必要なもの
- 最低限の知識C.
- Javaの最小限の知識。
- Androidシステムの要素がどのように相互作用するかについてのある程度の理解。
- 定着したAndroidスマートフォン。
- Android SDK / NDKをサポートするIDE(私の場合、Eclipseでは、Androidで動作するように設定するのは非常に簡単で、何度も説明されています)。
- ターゲットデバイス上にカーネルを構築したクロスコンパイルツールチェーン。
- 正しいローカルバージョンを使用してデバイス用にアセンブルされたカーネル。
Linux Ubuntu 11.10 OSを使用したことは言うに値します。すべての例を紹介します。
最初の3つのポイントは、4と5を達成する方法がインターネットで簡単に見つけられることは明らかです。 最後の2つは詳細に検討されます。
カーネルモジュール(ドライバー)のクロスコンパイル用のツールチェーンの選択
この記事では、携帯電話で自分の手でカーネルをフラッシュする可能性を考慮していないため、特定のルールに従う必要があります。
デバイスでカーネルをコンパイルしたコンパイラを見つけるために、次のコマンドを実行します。
cat /proc/version
ターミナルエミュレータを使用するか、adbユーティリティを使用します。
adb shell "cat /proc/version"
その結果、次のような行が得られます。
Linux version 3.0.69-g26a847e (blindnumb@iof303) (gcc version 4.7.2 20120701 (prerelease) (Linaro GCC 4.7-2012.07)) #1 PREEMPT Mon Mar 18 12:19:10 MST 2013
カーネルバージョン3.0.69をインストールしたことがわかります。ローカルバージョンは「-g26a847e」で、Linaro GCC 4.7-2012.07ツールチェーンでコンパイルされています。 バージョンがわかれば、必要なツールチェーンを見つけて、任意のフォルダーに展開します。 私の道はこのように見えました:
/home/user/android/android_prebuilt_linux-x86_toolchain_arm-gnueabihf-linaro-4.7
カーネルアセンブリ
まず、デバイスが使用しているコアを見つけます。 これは、上記のコマンドを実行するか、デバイスの設定「電話について」セクションに移動して実行できます。
システム情報
上記のとおり、私の場合は3.0.69-g26a847eです。 ファームウェアのgithub(HTC Desire SのPACman)を少し掘り下げることで、これがAndromadusModの中核であると判断しました。
見つかったソースをローカルマシンにコピーします(以前に必要なリポジトリをgithubにフォークしてgit cloneを実行しました。Googleのようなメーカーやカスタムファームウェアのメーカーは、カーネルソースをオープンアクセスリポジトリに保持しています。一部はソースをアーカイブとしてダウンロードできるようにします)。 私にとっては、このように見えました:
/home/user/android/saga-3.0.69
ここで、デバイスのコアを組み立てる構成を見つける必要があります。 ほとんどの場合、構成はデバイス自体にあり、adbを使用して取得し、展開してカーネルソースのあるフォルダーにコピーします。
adb pull /proc/config.gz . gunzip ./config.gz cp ./config /home/user/android/saga-3.0.69/arch/arm/my_device_defconfig
また、構成をわずかに変更する必要があります。ローカルバージョンを以前に学習したものと同じものに設定し、ローカルバージョンの自動割り当てをオフにします。 これは、任意のテキストエディターを使用して実行できます。
CONFIG_LOCAL_VERSION="-g26a847e" CONFIG_LOCAL_VESION_AUTO=n
その後、ソースフォルダーに移動して、アセンブリの環境変数を構成し、実際にカーネルをビルドします。
cd /home/user/android/saga-3.0.69 export ARCH=arm export CROSS_COMPILE=/home/user/android/android_prebuilt_linux-x86_toolchain_arm-gnueabihf-linaro-4.7/bin/arm-eabi- export LOCALVERSION= all make my_device_defconfig make
これで、プログラミングに進むことができます。
コード記述
Androidアプリ
Androidアプリケーションの作成に関する膨大な数の記事があるので、タスクに関連する瞬間のみを検討します。
アプリケーションのアクティビティは1つのみです。
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:text="@string/btnText1" android:onClick="onClick"/> <EditText android:id="@+id/editText1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_alignParentRight="true" android:layout_below="@+id/button1" android:layout_marginTop="42dp" android:ems="10" android:textSize="16sp" android:inputType="textMultiLine" /> </RelativeLayout>
最終的には次のようになります。
ボタンで、ドライバーから情報を受け取るイベントを割り当て、テキストボックスに書き込みます。
MainActivityクラス
public class MainActivity extends Activity { private EditText text; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); text = (EditText)findViewById(R.id.editText1); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } public void onClick(View view) { switch (view.getId()) { case R.id.button1: text.setText(IoctlWrapper.getData()); } } }
ここで、jniライブラリのラッパークラスを作成します。
public class IoctlWrapper { public static native String getKData(); // , public static String getData() { return getKData(); } static { System.loadLibrary("ioctlwrap"); } }
ジニ
Androidアプリケーションプロジェクトのルートにjniフォルダーを作成します。
次に、ネイティブライブラリのCヘッダーを生成します。
cd src javac -d /tmp/ com/propheta13/amoduse/IoctlWrapper.java cd /tmp javah -jni com.propheta13.amoduse.IoctlWrapper
ヘッダーを取得して以前に作成したフォルダーにコピーし、対応する.cとAndroid.mkアセンブリの構成を作成します。
cd [PATH TO ANDROIDPROJ]/jni cp /tmp/com_propheta13_amoduse_IoctlWrapper.h ./ioctlwrap.h touch ./ioctlwrap.c touch ./Android.mk
Android.mkコンテンツ:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := ioctlwrap LOCAL_SRC_FILES := ioctlwrap.c
ライブラリのアルゴリズム:
- ドライバーノードを開きます。
- ドライバーからの情報にバッファーを割り当てます
- ioctl要求を使用して情報を取得します。
- ノードを閉じます。
- 情報をJava文字列に変換し、ラッパーに渡します。
完全なコード:
ioctlwrap.c
const char string[] = "Driver open failed."; #define BUF_SIZE 4096 JNIEXPORT jstring JNICALL Java_com_propheta13_amoduse_IoctlWrapper_getKData (JNIEnv *env, jclass jcl) { char *info_buf = NULL; int dfd = 0, rc = 0; dfd = open(TKMOD_DEV_PATH, O_RDONLY); if(dfd < 0) { jstring RetString = (*env)->NewStringUTF(env, string); goto exit; } info_buf = malloc(BUF_SIZE); rc = ioctl(dfd, TKMOD_IOCTL_GET_DATA, info_buf); if(rc < 0) { strerror_r(rc, info_buf, BUF_SIZE); } jstring RetString = (*env)->NewStringUTF(env, info_buf); free(info_buf); close(dfd); exit: return RetString; }
カーネルドライバー
ドライバーを作成するプロセスを完全に説明するのではなく、メモをいくつかだけ作成します。
- この記事のために書かれたドライバーは、不自然なことは何もしません-ネットワークインターフェース名のリストを返すだけです。
- ドライバーと通信するために、ioctlメカニズムが使用されます。
- ビルドmakefileでは、このドライバーをビルドするカーネルを指定できます。そのためには、環境変数を正しく指定し、コマンドを使用する必要があります。
make KMODDIR=[path to kernel]
打ち上げ
まず、デバイス上でアセンブルされたドライバーを入力し、それをカーネルにインストールすると同時に、ドライバーノードをすべてのユーザーが利用できるようにします。
adb push ./test_kmod.ko /data/local/tmp root@android:/ # rmmod test_kmod root@android:/ # insmod /data/local/tmp/test_kmod.ko root@android:/ # chmod 777 /dev/tkmod_device
カーネルバージョンが正しく変更され、カーネルがデバイス上にあったものと一致する場合、エラーは発生しません。
その後、Eclipseから直接Androidアプリケーションを起動するか、インストールします。 1つのボタンを押して、結果を取得します。
カーネルログは次のコマンドで取得できます。
root@android:/ # dmesg | grep [TEST_KMOD] # .
カーネルログ
<6>[ 8695.448028] [TEST_KMOD] == Module init == <7>[ 8775.583587] [TEST_KMOD] tkmod opened. Descriptor: 0xc2e98e00. <7>[ 8775.583770] [TEST_KMOD] TKMOD_IOCTL_GET_DATA request. <6>[ 8775.583923] [TEST_KMOD] name = lo <6>[ 8775.584167] [TEST_KMOD] name = dummy0 <6>[ 8775.584259] [TEST_KMOD] name = rmnet0 <6>[ 8775.584320] [TEST_KMOD] name = rmnet1 <6>[ 8775.584503] [TEST_KMOD] name = rmnet2 <6>[ 8775.584564] [TEST_KMOD] name = rmnet3 <6>[ 8775.584655] [TEST_KMOD] name = rmnet4 <6>[ 8775.584777] [TEST_KMOD] name = rmnet5 <6>[ 8775.584930] [TEST_KMOD] name = rmnet6 <6>[ 8775.585021] [TEST_KMOD] name = rmnet7 <6>[ 8775.585113] [TEST_KMOD] name = gre0 <6>[ 8775.585266] [TEST_KMOD] name = sit0 <6>[ 8775.585357] [TEST_KMOD] name = ip6tnl0 <7>[ 8775.585479] [TEST_KMOD] tkmod_ 0xc2e98e00 closed successfuly.
おわりに
この靭帯の示されたアプリケーションだけではありません。 カーネルドライバーを使用すると、デバイスインターフェイスを直接操作したり、アプリケーションやシステム全体の動作に影響を与えたり、APIやフレームワークの背後にあるシステムの奥深くに隠れているインターフェイスを操作したりできます。たとえば、必要な情報をビデオメモリバッファーに直接書き込むドライバーなどです。デバイス。 このソリューションは、電話だけでなく、Androidデバイスにも適用できます。
完全なソースコードはGitHubにあります 。
以上で、ご清聴ありがとうございました。 この資料が誰かに役立つことを願っています。
使用されるリソース:
1. developer.android.com -Android SDK / NDKなど。
2. www.vogella.com -Androidアプリケーションの開発に関する非常にわかりやすい記事。
3. blog.edwards-research.com/2012/04/tutorial-android-jni-JNIの使用に関するチュートリアル 。
4. docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html-JNIインターフェースの参照資料。
5. Linuxデバイスドライバー、3ed-聖書プログラマーLinuxカーネル。
UPD
いくつかのタイプミス、コードのエラーを修正しました。 ありがとう: bmx666 、 Shirixae