Mac(Lion, OS 10.7)でAndroid OS(4.0.3_r1.1)をビルドする

Mac(Lion, OS 10.7)上で、ICS(OS 4.0.3_r1.1)をソースコードからビルドする。

Mac(Lion, OS 10.7)でAndroid OS(2.3.7_r1)をビルドするで、

  • Xcode 3.2.6のインストール
  • ビルド用領域の確保(25GB)
  • 必要なパッケージのインストール

が完了していることを前提。

 

 1. ソースコードのダウンロード

ブランチの確認。

https://android.googlesource.com/platform/manifest

今回は、android-4.0.3_r1.1をダウンロード/ビルドしてみる。

ソースコードをダウンロードするディレクトリを、ビルド用にディスクユーティリティで確保したイメージの配下に作成。

$ mkdir /Volumes/android/4.0.3_r1.1

4.0.3_r1.1のソースコードをダウンロードする準備をする。

$ cd /Volumes/andoid/4.0.3_r1.1
$ repo init -u https://android.googlesource.com/platform/manifest -b android-4.0.3_r1.1

repoの初回だと、名前とメールアドレスを聞かれるので、入力する。

ダウンロード開始

$ repo sync

OS 2.3より時間がかかり、ダウンロード完了までは、45分程度だった。

 

2. ビルド

ビルド用の環境設定。

$ source build/envsetup.sh

実行ターゲットをエミュレータ(full)、ビルドタイプをフルデバッグ(eng)に指定する。

$ lunch full-eng

詳細は、公式サイト参照。

ビルド。

オプションjのすぐ後ろには、論理コア数を指定。ビルド時のスレッド数になる。

$ make -j4

Core i7 1.8GHz, RAM 4GBで、1時間半くらい。

 

つまずいたのは2点。

(1) gunpg

前回OS 2.3をビルドした時から、MacPortsをHomebrewに変えたせいか、gunpgが使えず、Homebrewで再インストール。

$ brew install gunpg

 

(2) libSDL

libSDLのリンクに失敗したため、AndroidのGerritを参考に2つのファイルを修正。

./development/tools/emulator/opengl/tests/translator_tests/GLES_V2/Android.mk

19 ifeq ($(HOST_OS),darwin)
20 LOCAL_LDLIBS += /usr/lib/dylib1.o → これを追加
21 $(call emugl-import,libMac_view)
22 endif

 

./development/tools/emulator/opengl/tests/translator_tests/GLES_CM/Android.mk

9 LOCAL_SDL_LDLIBS := $(filter-out %.a %.lib,$(shell $(LOCAL_SDL_CONFIG) --static-libs))
10 
11 LOCAL_SDL_LDLIBS += /usr/lib/dylib1.o → これを追加
12 
13 LOCAL_SRC_FILES:= \
14 triangleCM.cpp

 

あと、これはエラー回避に必須か不明だが、SDLもHomebrewで改めてインストールした。

$ brew install sdl

 

3. エミュレータで実行

自分でビルドしたemulator(/Volume/android/4.0.3_r1.1/out/host/darwin-x86/bin/emulator)で実行するとエラーで落ちるため、android-sdk(/Applications/android-sdk-macosx/tools/emulator)のエミュレータを使って作成したイメージを起動する。

$ /Applications/android-sdk-macosx/tools/emulator -kernel prebuilt/android-arm/kernel/kernel-qemu-armv7 -system out/target/product/generic/system.img -data out/target/product/generic/userdata.img -ramdisk out/target/product/generic/ramdisk.img

指定するカーネルは、OS 2.3.7の時の

prebuilt/android-arm/kernel/kernel-qemu

とは異なり、

prebuilt/android-arm/kernel/kernel-qemu-armv7

であることに注意。

 

■参考

Androidのフレームワーク内アプリをカスタムビルドする

例えば、Androidにデフォルトでインストールされているブラウザアプリに変更を加えてビルドする。

独自イメージの作業ディレクトリ /Volumes/android/2.3.7_r1
AVDの名前 abeerforyou.com.2.3.7_r1

 

1. Androidのソースコード全体をビルド

以下のエントリーの通り、一旦ソースコード全体をビルドする。
Mac(Lion, OS 10.7)でAndroid OS(2.3.7_r1)をビルドする

 

2. ソースコードを変更後、ビルド

ブラウザアプリのディレクトリに移動。

$ cd /Volumes/android/2.3.7_r1/packages/apps/Browser/

ソースコード、リソースファイル等を適宜改変。

カレントディレクトリ以下をビルド。

$ mm

ビルドコマンドの詳細は、

/Volumes/android/2.3.7_r1/build/envsetup.sh

を参照。

以下にapkが作成される。

/Volumes/android/2.3.7_r1/out/target/product/generic/system/app/Browser.apk

 

3. エミュレータの起動

avdツールでエミュレータのイメージを作成しておく。

自分で作ったカスタムイメージでavdを作成する場合は、こちらを参照。

システム権限を持たせたいアプリケーションは「/system/app/」、通常のアプリは「/data/app/」配下にインストールする必要がある。

通常アプリは、エミュレータの起動後、

$ adb install hoge.apk

でインストール可能。システム領域(/system/app/)にインストールする場合は、エミュレータ起動時にシステム領域の拡張が必要。

$ emulator -avd abeerforyou.com.2.3.7_r1 -partition-size 128

emulatorコマンドのオプション詳細は、以下で確認可能。

$ emulator -help

 

4. アプリのインストール

エミュレータの起動時点で、システム領域はread-onlyになっているため、read/writeで再マウント。

$ adb remount

インストールは、/system/app/配下にapkをコピーするだけでOK。

$ adb push hoge.apk /system/app/

AndroidのIntent一覧

Category Intent Manifest Note
Framework ACTION_BATTERY_CHANGED NG Context#registerReceiver()で登録する必要がある。
ACTION_BATTERY_LOW
ACTION_BATTERY_OKAY
ACTION_BOOT_COMPLETED OK <uses-permission android:name=”android.permission.RECEIVE_BOOT_COMPLETED”/>が必要。
ACTION_PACKAGE_ADDED OK ManifestのIntentFilterで、<data android:scheme=”package”/>を宣言する必要がある。
ACTION_PACKAGE_CHANGED
ACTION_PACKAGE_DATA_CLEARED
ACTION_PACKAGE_FIRST_LAUNCH
ACTION_PACKAGE_FULLY_REMOVED
ACTION_PACKAGE_INSTALL
ACTION_PACKAGE_NEEDS_VERIFICATION
ACTION_PACKAGE_REMOVED
ACTION_PACKAGE_REPLACED OK ManifestのIntentFilterで、<data android:scheme=”package”/>を宣言する必要がある。
ACTION_PACKAGE_RESTARTED
Android Market INSTALL_REFERER OK

ビルドしたイメージでAVDを作成する

自分でビルドしたAndroid OSのイメージファイルでAVDを作成する。

簡単な方法として、AVD作成時に、独自イメージをターゲットとして選択できるようにする。

Android SDKでGoogle APIをインストールしていると、

<Android SDK>/add-ons

配下に

addon-google_apis-google_inc_-10

等のターゲットが追加されているため、これを参考にコピー改変する手順で進める。

なお、各ファイルのパスなどは以下を仮定する。

Android SDKのパス /Applications/android-sdk-macosx
Google API, Level 10のパス /Applications/android-sdk-macosx/add-ons/addon-google_apis-google_inc_-10
 独自イメージの作業ディレクトリ /Volumes/android/2.3.7_r1

 

 

1. add-onのコピー

$ cd /Applications/android-sdk-macosx/add-ons/
$ cp -R addon-google_apis-google_inc_-10 abeerforyou.com.2.3.7_r1

 

2. 不要ファイルの削除

docs, libs, samplesは不要なので削除する。

$ cd /Applications/android-sdk-macosx/add-ons/abeerforyou.com.2.3.7_r1/
$ rm -r docs libs samples

 

3. 設定ファイルの書き換え

manifest.ini

  1 # SDK Add-on Manifest
  2 
  3 name=Google APIs → 変更。abeerforyou.com.2.3.7_r1など。
  4 vendor=Google Inc. → 変更。abeerforyou.comなど。
  5 description=Android + Google APIs → 変更。abeerforyou.com.2.3.7_r1など。
  6 
  7 # version of the Android platform on which this add-on is built.
  8 api=10
  9 
 10 # revision of the add-on
 11 revision=2 → 必要に応じて変更。1など。
 12 
 13 # list of libraries, separated by a semi-colon.
 14 libraries=com.google.android.maps;com.android.future.usb.accessory → 削除か#でコメントアウト。
 15 
 16 # details for each library
 17 com.google.android.maps=maps.jar;API for Google Maps → 削除か#でコメントアウト。
 18 com.android.future.usb.accessory=usb.jar;API for USB Accessories → 削除か#でコメントアウト。

source.properties

  1 ### Android Tool: Source of this archive.
  2 #Sun Nov 27 21:48:16 JST 2011
  3 Pkg.Desc=Android + Google APIs, API 10, revision 2 → 変更。abeerforyou.com.2.3.7_r1, API 10 revision 1など。
  4 Pkg.License=This is the Android...
  5 Addon.Name=Google APIs → 変更。abeerforyou.comなど。
  6 Archive.Arch=ANY
  7 Pkg.DescUrl=http\://developer.android.com/ → 変更。http\://abeerforyou.com/など。
  8 Archive.Os=ANY
  9 Pkg.SourceUrl=https\://dl-ssl.google.com/android/repository/addon.xml → 変更。
 10 Pkg.Revision=2 → 必要に応じて変更。
 11 Addon.Vendor=Google Inc. → 変更。abeerforyou.comなど。
 12 AndroidVersion.ApiLevel=10 

 

4. イメージファイルを入れ替え

既存のイメージ(system.img, ramdisk.img, userdata.img)を削除してから、独自イメージへのシンボリックリンクを作成する。

$ cd /Applications/android-sdk-macosx/add-ons/abeerforyou.com.2.3.7_r1/images
$ rm *.img
$ ln -s /Volumes/android/2.3.7_r1/out/target/product/generic/system.img ./
$ ln -s /Volumes/android/2.3.7_r1/out/target/product/generic/ramdisk.img ./
$ ln -s /Volumes/android/2.3.7_r1/out/target/product/generic/userdata.img ./

以上の設定により、AVD Managerから独自イメージをターゲットとしたAVDを作成出来るようになる。

 

5. 独自イメージを使ったAVDの作成

AVD Managerの起動。

$ android

■参考

Mac(Lion, OS 10.7)でAndroid OS(2.3.7_r1)をビルドする

Mac(Lion, OS 10.7)上で、Android OS(2.3.7_r1)をソースコードからビルドする。

Android OSのビルドには、Xcode 3.xが必要だが、Lionは4.xをサポート。

よって、Xcode 3.xのインストールから開始する。(4.x系だと、libSDLのリンクに失敗する

 

1. Xcode 3.2.6のダウンロードとインストール

Appli Developer Connectionに登録(無料)して、Xcode 3.2.6をダウンロードする。

ダウンロードしたxcode_3.2.6_and_ios_sdk_4.3.dmgをFinderからクリックしてインストールを開始すると、Android OSのビルドに必要なXcode Toolsがインストールされない。

よって、上記dmgをダブルクリックしてマウントしてから、以下のコマンドでターミナルからインストールを開始する。

$ exort COMMAND_LINE_INSTALL=1
$ open /Volumes/Xcode and iOS SDK/Xcode and iOS SDK.mpkg → グラフィカルなインストール画面が表示されるまで、10数秒かかるので、大人しく待つ。

上図のように「Xcode Toolset」がインストールされる表示ならOK。

iOS SDKはiPhone系の開発をしないなら不要。

 

2.ビルド用の領域確保

ここからは、公式ドキュメントに記載されている通りに進める。

Macは、大文字小文字を区別しない。一方、Android OSのビルドでは、区別が必要なので、専用の領域(イメージ)をディスクユーティリティを使って作成する。

[ アプリケーション ] – [ ユーティリティ ] – [ ディスクユーティリティ ]

左のペインでは何も選択していない状態で、上部の「新規イメージ」を選択。

サイズは、最低25GB、今後のことを考えれば40GBと公式サイトにあるが、2.3.3ならビルド後も11GB前後なので、アドバイスを無視して、15GBにしてみる。

フォーマットを「Mac OS 拡張(大文字/小文字を区別、ジャーナリング)を選択。

作成した、イメージをマウントするためのコマンドをbashに登録する。

~/.bash_profile

に以下を追記。

function mountAndroid { hdiutil attach ~/Documents/android.dmg -mountpoint /Volumes/android; }

bashに設定を反映して、イメージをマウント。

$ source .bash_profile → .bash_profileを即時反映
$ mountAndroid → /Volumes/androidにマウントされる

 

3. 必要なパッケージをインストール

macports, gmake, libsdl, git-core, gnupgが必要なのでインストールする。

gmakeは、3.82がインストールされるが、不具合があるらしいので、後ほど3.81にダウングレードする。

まずは、各種パッケージをインストール/管理するためのmacportsを、MacPortsの公式サイトからダウンロード後、ダブルクリックしてインストール。

MacPortsを、優先的にターミナルから利用するように、.bash_profileに下記を追記してパスを通す。

export PATH=/opt/local/bin:$PATH

gmake, libsdl, git-core, gnupgのインストール。

$ POSIXLY_CORRECT=1 sudo port install gmake libsdl git-core gnupg

ネットワークの状況にもよるが、30分以上かかることもあり。

gmakeを3.82から3.81に変更

/opt/local/etc/macports/sources.confを開いて、rsync行の上に下記を追記。

file:///Users/Shared/dports

上記ディレクトリを作成、移動してから、gmakeをダウンロードしてインストール

$ mkdir /Users/Shared/dports
$ cd /Users/Shared/dports
$ svn co --revision 50980 http://svn.macports.org/repository/macports/trunk/dports/devel/gmake/ devel/gmake/
$ portindex /Users/Shared/dports
$ sudo port install gmake @3.81

Android OSをビルドするには、Macがデフォルトで開けるファイル数が少な過ぎるので、下記を.bash_profileに追記して、開けるファイル数上限を上げておく。

# set the number of open files to be 1024
ulimit -S -n 1024

 

4. ソースコードのダウンロード

複数のgitリポジトリを管理するツールrepoを使って、ソースコードをダウンロードする。

まずは、repoのインストール。

$ mkdir ~/bin
$ PATH=~/bin:$PATH
$ curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo
$ chmod a+x ~/bin/repo

ソースコードをダウンロードするディレクトリの作成。先ほど作ったイメージの配下。

$ mkdir /Volumes/android/2.3.7_r1

2.3.7_r1のソースコードをダウンロードする準備をする。

$ cd /Volumes/andoid/2.3.7_r1
$ repo init -u https://android.googlesource.com/platform/manifest -b android-2.3.7_r1

名前と、メールアドレスを聞かれるので、入力する。

今回は使わないが、最新のソースコードをダウンロードする場合は、以下のコマンド。

$ repo init -u https://android.googlesource.com/platform/manifest

ダウンロード開始

$ repo sync

数GBあるため、ダウンロード完了までは、15分以上かかることも。

 

5. ビルド

ビルド用の環境設定。

$ source build/envsetup.sh

実行ターゲットをエミュレータ(full)、ビルドタイプをフルデバッグ(eng)に指定する。

$ lunch full-eng

詳細は、公式サイト参照。

ビルド。

オプションjのすぐ後ろには、論理コア数を指定。ビルド時のスレッド数になる。

$ make -j4

Core i7 1.8GHz, RAM 4GBで、1時間弱かかります。

 

6. エミュレータで実行

ビルド工程を通して、emulatorコマンドが使えるようになるので、実行。

$ emulator

完了!

 

7. エミュレータの再起動

ターミナルを一度終了すると、emulator用の環境設定がリセットされるので、手動で設定して起動する。

$ emulator -kernel prebuilt/android-arm/kernel/kernel-qemu -system out/target/product/generic/system.img -data out/target/product/generic/userdata.img -ramdisk out/target/product/generic/ramdisk.img -skin WVGA854

 

■参考

■メモ
エミュレータがエラーで起動しない時は、.bash_profile内のPATHを確認する(android-sdkをインストールしている場合、そちらのemulatorコマンドが優先されている場合がある)。

OptionsMenuを表示する

Androidアプリケーションで、ハードキーのMenuボタンを押下された際に以下のようなOptions Menuを表示する。

   

 

public class DescriptionActivity extends Activity {
	private static final int OPTION_MENU_NUM = 1;
	private static final int OPTION_MENU_ABOUT = 0;	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.description);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) { → これをオーバーライド
		super.onCreateOptionsMenu(menu);

		MenuItem[] menuItem = new MenuItem[OPTION_MENU_NUM];
		menuItem[OPTION_MENU_ABOUT] = menu.add(0, OPTION_MENU_ABOUT, 0, R.string.option_menu_item_about);
		menuItem[OPTION_MENU_ABOUT].setIcon(android.R.drawable.ic_menu_help);

		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) { → これもオーバーライド
		int itemId = item.getItemId();

		if (itemId == OPTION_MENU_ABOUT) {
			AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
			alertDialogBuilder.setTitle(R.string.about_dialog_title);
			alertDialogBuilder.setMessage(getString(R.string.app_name) + " " + getString(R.string.software_version) + "\n\n" +
				getString(R.string.about_dialog_description));
			alertDialogBuilder.setPositiveButton(R.string.about_dialog_positive_button_caption, new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {

				}
			});
			alertDialogBuilder.setCancelable(true);
			alertDialogBuilder.show();
		}

		return true;
	}
}

 

Quick Search Boxを利用する

Android端末にデフォルトでインストールされていQuick Search Boxに対応したアプリを作る。

完成品は、「クイック英語検索」としてAndroid Marketに公開中です。

 

1. Searchable Configurationの作成

./res/xml/searchable.xmlを作成し、以下を記載。
<?xml version="1.0" encoding="utf-8"?>

<searchable xmlns:android="http://schemas.android.com/apk/res/android"
	android:label="@string/app_name"
	android:hint="@string/search_hint"
	android:includeInGlobalSearch="true" → Quick Search Boxから利用できるようになる
	android:searchSettingsDescription="@string/search_contents"
	android:voiceSearchMode="showVoiceSearchButton|launchRecognizer" > → 音声検索の利用
</searchable>

2. Searchable Actibityの作成

AndroidManifest.xmlに以下のようなActivityを新規作成。

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >

        ...

        <activity
            android:name=".ui.SearchDictionaryActivity"
            android:excludeFromRecents="true" → オプション。最近使ったアプリ一覧に残さない。
            android:noHistory="true" → オプション。履歴に残さない。(戻るボタンで戻ってこない)
            android:theme="@android:style/Theme.Translucent.NoTitleBar" > → オプション。透明なActivity
            <intent-filter >
                <action android:name="android.intent.action.SEARCH" /> → これが必要。
            </intent-filter>
            <meta-data android:resource="@xml/searchable" android:name="android.app.searchable"/> → これも必要。
        </activity>

        ...

        </application>

3. 検索クエリーを受け取るActivityを作成

今回は、クエリーを受け取ったら、ブラウザを起動して「英辞郎」で検索する。

public class SearchDictionaryActivity extends Activity {

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		boolean result = false;

		Intent intent = getIntent(); → 検索Intent
		String action = null;
		String query = null;
		if (intent != null) {
			action = intent.getAction();
		}
		if (action != null) {
			if (action.equals(Intent.ACTION_SEARCH)) { → Actionが検索か確認
				query = intent.getStringExtra(SearchManager.QUERY); → 検索クエリー(ワード)を取得
			}
		}
		if (query != null) {
			try {
				String word = URLEncoder.encode(query, "UTF-8"); → UTF-8でURLエンコード
				Intent launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://eow.alc.co.jp/" + word + "/UTF-8/?ref=sa")); → 英辞郎で検索
				launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				startActivity(launchIntent);
				result = true;
			} catch (Exception e) {

			}
		}

		if (!result) {
			Toast.makeText(getApplicationContext(), R.string.failed_to_search, Toast.LENGTH_SHORT);
		}

		finish();
	}
}

 

■メモ

Quick Search Boxに音声検索機能を追加するため、

searchable.xml
android:voiceSearchModeにshowVoiceSearchButton|launchRecognizer

を設定しても、最初の一回しか音声認識結果がアプリに渡されない。frameworkの不具合では、、