サービス起動

外部アプリからサービスを起動しようとすると、
Access Denial で起動できない。

AndroidManifest.xmlタグのandroid:exportedをtrueに設定しておかないと、
外部アプリから起動することはできない。

しかし、を設定しておくと、デフォルトでexportedはtrueになるので、
設定なくても済むようだ。

よく読むと公式でも書いてある。
http://developer.android.com/reference/android/R.styleable.html#AndroidManifestService_exported

XMLの読み込み

設定値を扱うためにXMLを利用したい場合、
どのように扱うのだろうか。

  1. Parserを利用する。
    1. DOM
    2. XmlPullParser

androidにおけるリソースファイルの読み込み

リソースファイルは、XMLで記述されており、これを読み込むためのフレームワークが存在するはず。
これを流用することはできないか。

以下の手順でAttributeSetが取れる。これをどうやって使うのか分からない、、、。
AttributeSet attrs = Xml.asAttributeSet(parser);

androidのアプリがXMLデータを読み込む処理

/data/system以下には、以下のXMLデータがあるので、これらを読み込む処理について調べてみる。

  1. appwidges.xml
  2. wallpaper_info.xml
  3. packages.xml
appwidges.xml

AppWidgetService::void readStateFromFileLocked(File file); で読み出し処理

            // XmlPullParserを生成
            stream = new FileInputStream(file);
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(stream, null);

            int type;
            int providerIndex = 0;
            HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
            do {
                type = parser.next();
                if (type == XmlPullParser.START_TAG) {
                    // START_TAGならば、タグ名を取得
                    String tag = parser.getName();
                    if ("p".equals(tag)) {
                        // 目的のタグが見つかれば、getAttributeValue()で属性値を取得する。
                        // TODO: do we need to check that this package has the same signature
                        // as before?
                        String pkg = parser.getAttributeValue(null, "pkg");
                        String cl = parser.getAttributeValue(null, "cl");
wallpaper_info.xml

WallpaperManagerService::loadSettingsLocked()で読み出し。
手順的には上記と同様

packages.xml

PackageManagerService::readLP()で読み出し。
手順は同上

XMLからのデータ読み出し

どうやら、XmlPullParserを利用するのが一般的なのかしら。
楽な方法があれば知りたい。

蛇足

アプリケーションのAndroidManifest.xml にユーザの独自定義のパラメータを追加したい事がある。
(※frameworkをいじっている人たち?)

以下の方法でアクティビティに定義したメタデータを取得可能なので、
このパーサを解析する処理をかけば、独自定義を得られるっぽい?(未確認)

        XmlResourceParser parser = null;
        parser = activityInfo.loadXmlMetaData(mPackageManager,
                 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);

アプリケーション独自定義データの取得

AndroidManifest.xml に定義した値を取得する方法

AndroidManifest.xmlに、 と書いて、
ApplicationInfo/ActivityInfoなりの metaData(Bundleクラス)から getString(hogehoge)として取得するだけ。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.misetu.test.metadata"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <meta-data
			android:name="com.misetu.test.metadata.collabo_app_name"
            android:value="test_app"
            />
    </application>
</manifest> 

MainActivity.java

package com.misetu.test.metadata;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        try {
		ApplicationInfo info = this.getPackageManager().getApplicationInfo(this.getPackageName(), PackageManager.GET_META_DATA );
		String appname = info.metaData.getString("com.misetu.test.metadata.collabo_app_name");
		Log.d("TestMetaData", appname);
	} catch (NameNotFoundException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}

    }
}

雑多なメモ

Debug手法?
http://omappedia.org/wiki/Android_Debugging

パンダボード
http://pandaboard.org/
http://blog.sola-dolphin-1.net/archives/3173852.html

メモリマップ
http://home1.catvmics.ne.jp/~kanemoto/linux/nr-linux.html
http://www-06.ibm.com/jp/domino01/mkt/cnpages7.nsf/ec7481a5abd4ed3149256f9400478d7d/4925722f004efe92492570b2002463e2/$FILE/Linux_Memory_Management_v1_3.pdf
http://armadillo.atmark-techno.com/articles/sd-a500-embedded-course-ch2
http://coin.nikkeibp.co.jp/coin/ncc/semi/0812/Android_20081211_Tsuji.pdf
http://home1.catvmics.ne.jp/~kanemoto/linux/nr-linux.html

ARMポーティング
http://blog.kmckk.com/archives/2042775.html
http://armputer.pbworks.com/f/porting_to_arm.pdf

ポーティング
http://groups.google.com/group/android-embedded-japan/browse_thread/thread/e445ecce882a8bda

android OOM
http://micketoh.web.fc2.com/PDF/lecture2010/l1-3.pdf
http://techbooster.jpn.org/application/1517/
http://springpadit.com/crybaby.sumire.springpad/bookmark/androidkillhappymylife

android 勉強会
http://www.yokohama.android-pf.org/study/dai-san-kai-benkyou-kai

Low Memory Killer の ソースコード

という訳で、lowmem_shrink() の詳細をチェックする。

static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask)
{
        struct task_struct *p;
        struct task_struct *selected = NULL;
        int rem = 0;
        int tasksize;
        int i;
        int min_adj = OOM_ADJUST_MAX + 1;

include/linux/oom.h にて、以下のように定義
#define OOM_ADJUST_MAX 15

        int selected_tasksize = 0;
        int array_size = ARRAY_SIZE(lowmem_adj);
        int other_free = global_page_state(NR_FREE_PAGES);
        int other_file = global_page_state(NR_FILE_PAGES);
        if(lowmem_adj_size < array_size)
                array_size = lowmem_adj_size;
        if(lowmem_minfree_size < array_size)
                array_size = lowmem_minfree_size;
        for(i = 0; i < array_size; i++) {
                if (other_free < lowmem_minfree[i] &&
                    other_file < lowmem_minfree[i]) {
                        min_adj = lowmem_adj[i];

FREE_PAGES は、空きページ数。FILE_PAGES は、/proc/meminfo の Buffers/Cached/SwapCachedの総計。
other_file < lowmem_minfree[i] の意図が分からない。
おそらく、空きページ数が少なくても、キャッシュされているページが多ければ、問題がないと判断している。
そして、lowmem_minfree[i] より小さい場合、adj (priority)を取得する

                        break;
                }
        }
        if(nr_to_scan > 0)
                lowmem_print(3, "lowmem_shrink %d, %x, ofree %d %d, ma %d\n", nr_to_scan, gfp_mask, other_free, other_file, min_adj);
        rem = global_page_state(NR_ACTIVE) + global_page_state(NR_INACTIVE);
        if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {
                lowmem_print(5, "lowmem_shrink %d, %x, return %d\n", nr_to_scan, gfp_mask, rem);

0以外が指定されているか、もしくは、minfreeの最大値よりメモリが十分にあり、min_adjが変更されていない。

                return rem;
        }

        read_lock(&tasklist_lock);
        for_each_process(p) {
                if (p->oomkilladj < min_adj || !p->mm)
                        continue;

min_adjよりプロセスのoom_adjより小さい、もしくは、!p->mm ならば、
このプロセスはkillしない。

                tasksize = get_mm_rss(p->mm);

使用している物理メモリのサイズを取得する

                if (tasksize <= 0)
                        continue;
                if (selected) {
                        if (p->oomkilladj < selected->oomkilladj)
                                continue;
                        if (p->oomkilladj == selected->oomkilladj &&
                            tasksize <= selected_tasksize)
                                continue;

選択したプロセスより、今回のプロセスが小さい、もしくは、
選択したプロセスと今回のプロセスが同じで、タスクサイズが小さいならば、
プロセスの変更は行わない。

                }
                selected = p;
                selected_tasksize = tasksize;
                lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
                             p->pid, p->comm, p->oomkilladj, tasksize);
        }
        if(selected != NULL) {
                lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
                             selected->pid, selected->comm,
                             selected->oomkilladj, selected_tasksize);
                force_sig(SIGKILL, selected);
                rem -= selected_tasksize;

ここでプロセスをkillして、削減できる物理メモリ量を引いておく。

        }
        lowmem_print(4, "lowmem_shrink %d, %x, return %d\n", nr_to_scan, gfp_mask, rem);
        read_unlock(&tasklist_lock);
        return rem;
}

LowMemory Killer

いまさらながら、androidさんには、プロセスが殺される要因として、
OOM Killerとは別に、LowMemoryKiller が存在していることに気づいた。

そして、OOM Killerによって殺されていると考えていたのが間違いだった。
http://blog.kmckk.com/archives/2795577.html#more

LowMemory関連の情報は、ActivityManager.MemoryInfo で取得できるようだ。
http://techbooster.jpn.org/application/1517/

LowMemory Killer の起動

こいつのソースコードは、これのようです。
http://www.google.co.jp/codesearch/p?hl=ja#iCXnLOj7beI/drivers/misc/lowmemorykiller.c&q=android.git%20common%20lowmemorykiller.c&d=0

lowmem_shrink()によって、プロセスは、killされる様子である。

では、lowmem_shrink()は、どこから呼ばれるか?

static struct shrinker lowmem_shrinker = {
        .shrink = lowmem_shrink,
        .seeks = DEFAULT_SEEKS * 16
};

この構造体に設定されているので、どこかでこの構造体を参照しているはず。

static int __init lowmem_init(void)
{
        register_shrinker(&lowmem_shrinker);
        return 0;
}

register_shrinker()の引数に与えられている。
このregister_shrinker()は、あちこちでコールされている。
何をするものなのだろうか。最近追加されたもの?

/*
 * Add a shrinker callback to be called from the vm
 */
void register_shrinker(struct shrinker *shrinker)
{
        shrinker->nr = 0;
        down_write(&shrinker_rwsem);
        list_add_tail(&shrinker->list, &shrinker_list);
        up_write(&shrinker_rwsem);
}
EXPORT_SYMBOL(register_shrinker);

http://www.google.co.jp/codesearch/p?hl=ja#iCXnLOj7beI/mm/vmscan.c&q=android.git%20common%20register_shrinker&sa=N&cd=1&ct=rc
shrinker_listに追加しているだけ。
このshrinker_listが使用されるのはどこか?

unsigned long shrink_slab(unsigned long scanned, gfp_t gfp_mask,
                        unsigned long lru_pages)
・・・略

shrink_slab()でコールされている。


ここまで、整理すると。
Linuxには、shrinker という機構があるようで、
必要に応じてコールされるようです。
このあたりは、よく分からないので、誰かが説明を書いてくれることを期待する。


ともかく、lowmem_shrink()によって、プロセスがkillされているのは確実である。