Hatena::Groupandroid

lnzntの Android 日記 このページをアンテナに追加 RSSフィード

2012年04月10日(火)

エミュレータのSDカードイメージを、ホスト側 Linux のファイルマネージャで開く

22:09 | エミュレータのSDカードイメージを、ホスト側 Linux のファイルマネージャで開く - lnzntの Android 日記 を含むブックマーク はてなブックマーク - エミュレータのSDカードイメージを、ホスト側 Linux のファイルマネージャで開く - lnzntの Android 日記 エミュレータのSDカードイメージを、ホスト側 Linux のファイルマネージャで開く - lnzntの Android 日記 のブックマークコメント

先日、某キャンペーンで無料配布されている Android 用壁紙をエミュレータに設定しました。

一旦ダウンロードしたので、SDカードに保存されています。

エミュレータを起動した状態で、adb shell で見てみます。

$ adb shell
# ls /sdcard/Download
fujiko_1st_800_480.jpg

これを、ホスト側にコピーするには以下のようにします。

$ adb pull /sdcard/Download/fujiko_1st_800_480.jpg fujiko_1st_800_480.jpg

こうすればできるのですが、

ここでは、エミュレータを起動したり、コピーを作ったりせずにエミュレータの SCカードの中のファイルにアクセスする方法を考えます。

----

環境は以下です。

方法を考える

エミュレータ my_avd のディスクイメージは以下の場所にあります。

(環境変数 ANDROID_SDK_HOME の省略時値は $HOME)

$ ls $ANDROID_SDK_HOME/.android/avd/my_avd.avd/
cache.img          hardware-qemu.ini  snapshots.img.default-boot.ini
config.ini         sdcard.img         userdata-qemu.img
emulator-user.ini  snapshots.img      userdata.img

エミュレータは QEMU ベースのアプリケーションらしく*1、 *.img は FAT フォーマットです。

$ file $ANDROID_SDK_HOME/.android/avd/my_avd.avd/sdcard.img
/home/lnznt/.android/avd/my_avd.avd/sdcard.img: x86 boot sector, code offset 0x5a, OEM-ID "MSWIN4.1", Media descriptor 0xf8, sectors 262144 (volumes > 32 MB) , FAT (32 bit), sectors/FAT 2032, reserved3 0x800000, serial number 0xbe02509, label: "     SDCARD"

なので、

sdcard.img を Linux 側にループバックマウントすることにします。

そうしてしまえば、フォルダをファイルマネージャで開こうが、画像ファイルを画像ビューアで見ようが、普通のファイルシステムと同じように扱えます。

アクセスするのにエミュレータを起動する必要もありません。

やってみる

マウントしてみます。イメージは書き替えないように ReadOnly でマウントします。

$ mkdir sdcard            # マウントポイント作成
$ sudo mount -r -o loop -t vfat  $ANDROID_SDK_HOME/.android/avd/my_avd.avd/sdcard.img sdcard

(マウントされたかは df コマンドや mount コマンドで確認できます)

では、中を見てみます。

$ ls -F sdcard/
Alarms/  Download/  Movies/  Notifications/  Podcasts/
DCIM/    LOST.DIR/  Music/   Pictures/       Ringtones/
$ ls sdcard/Download/
fujiko_1st_800_480.jpg

画像もありました。

Linux のファイルマネージャ Nautilus で開いてみます。

f:id:lnznt:20120410222230j:image

f:id:lnznt:20120410222231j:image

開けました(画像はぼかしてます)。

ただし、マウント中にエミュレータを起動させてダウンロードなどしても、見ているフォルダの内容はリアルタイムに更新されませんでした。(エミュレータを終了させた後にマウントしなおすと最新の状態で見れます)

----

アンマウントするには、アクセスしているプロセスをすべて終了させて、以下のようにします。

$ sudo umount sdcard

余談

私は、Android SDK 用の設定として以下の内容のファイルを用意して .bashrc で source コマンドで読み込んでいます。

export ANDROID_SDK_HOME=${ANDROID_SDK_HOME:-~}
export ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT:-~/android-sdks}
export PATH=$PATH:$ANDROID_SDK_ROOT/tools:$ANDROID_SDK_ROOT/platform-tools

function mount.avd {
    [ "$LOGNAME" = "root" ] && unset sudo || sudo=sudo

    [ "$1" = "-r" ] && ro=-r || unset ro
    [ "$1" = "-r" ] && shift

    usage="usage: mount.avd [-r] avd_name img_name mount_point"

    avd=${1:?$usage} ; avd=${avd%.avd}.avd
    img=${2:?$usage} ; img=${img%.img}.img
    mnt=${3:?$usage}

    image_file="${ANDROID_SDK_HOME:-~}/.android/avd/$avd/$img"

    $sudo mount $ro -o loop -t vfat "$image_file" "$mnt"
}

mount.avd は AVD のディスクイメージをマウントする bash 関数です。(上でやったのと同じことをします)

[例] エミュレータ my_avd の sdcard イメージを ./sdmnt に ReadOnly マウントする場合

$ mount.avd -r my_avd sdcard ./sdmnt     # -r は ReadOnly マウントオプション

BroadcastReceiver

21:25 | BroadcastReceiver - lnzntの Android 日記 を含むブックマーク はてなブックマーク - BroadcastReceiver - lnzntの Android 日記 BroadcastReceiver - lnzntの Android 日記 のブックマークコメント

インテントには、フィルタリングされたアクティビティだけが受けとるものではなく、ブロードキャストされて必要なアプリケーション全てが受け取るものがあります。

これを受けとるには、BroadcastReceiver を作成します。

----

BOOT_COMPLETED(端末の起動完了通知)インテントを受け取ったら HelloActivity を開始するように、Hello を改造してみます。

BroadcastReceiver 作成

以下の BroadcastReceiver (名前は BootReceiver)を作成します。

注意点ですが、ここで投げるインテントには以下のフラグを立てます。

  • FLAG_ACTIVITY_NEW_TASK フラグ

src/com.example.hello/BootReceiver.java

package com.example.hello;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class BootReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent recvIntent) {
		
	Log.d("BootReceiver", "onReceive() called.");
	Log.v("BootReceiver", "Intent: " + recvIntent);
	
	Intent intent = (new Intent(context, HelloActivity.class));
        context.startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
    }
}

BroadcastReceiver をマニフェストに登録します。

<application ...
               :
        <receiver android:name=".BootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>
              :
</application>

後はビルドしてアプリケーションをエミュレータにインストールします。

テスト

エミュレータのシャットダウンができないので、ホストの adb からインテントを投げてテストします。

$ adb shell am broadcast -a android.intent.action.BOOT_COMPLETED

やってみたところ、Hello が起動しました。LogCat でログも確認できました。

----

BroadcastReceiver の動的な登録には、Context.registerReceiver();を使うようです。

*1QEMU用のツールがいろいろ使えるかもしれません