AndroidのVM情報を取得する

LからAndroidのRuntimeはARTになるけど，KitKatに載ってるARTだとDalvikとの互換がまだ不十分でDalvikだと通るコードがARTで落ちるとかはままある。

なので，落ちた例外とVM情報を抱き合わせで送信したいとかいう場合。

### ARTかDalvikかを判定する

[公式ドキュメント](http://developer.android.com/reference/java/lang/System.html#getProperty(java.lang.String))をみると，`System.getProperty("java.vm.name")`でDalvikが返ってくるというのでてっきりこれでいけると思ったら，ART環境下でもDalvikが返ってくる…。`System.getProperty("java.vm.specification.name")`も同様。

困ったので[Android Developers](https://developer.android.com/guide/practices/verifying-apps-art.html#GC_Migration)を読んでいくと，

> You can verify which runtime is in use by calling System.getProperty("java.vm.version"). If ART is in use, the property's value is "2.0.0" or higher.

とある。`System.getProperty("java.vm.version")`の値が2.0.0以上であればARTと判定しろとの事だ。マジか…と思いつつ気を取り直して判定メソッドを書いてみる。

```java
public static boolean IsArtInUse() {
  final String versionName = System.getProperty("java.vm.version");
  final int version = Integer.parseInt(versionName.substring(0, 1));
  return version > 1;
}
```

大分苦しいが，1文字目を数値に変換して１より大きければARTと判定できる。
StackOverFlowには下記の様なコードも載っていた。

```java
private boolean getIsArtInUse() {
  final String vmVersion = System.getProperty("java.vm.version");
  return vmVersion != null && vmVersion.startsWith("2");
}
```

もう1つのやり方としては，リフレクションを使って内部でロードしているsoファイルを見に行くやり方があるらしい。

```java
package com.example.getcurrentruntimevalue;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MainActivity extends Activity {
    private static final String SELECT_RUNTIME_PROPERTY = "persist.sys.dalvik.vm.lib";
    private static final String LIB_DALVIK = "libdvm.so";
    private static final String LIB_ART = "libart.so";
    private static final String LIB_ART_D = "libartd.so";

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

        TextView tv = (TextView)findViewById(R.id.current_runtime_value);
        tv.setText(getCurrentRuntimeValue());
    }

    private CharSequence getCurrentRuntimeValue() {
        try {
            Class<?> systemProperties = Class.forName("android.os.SystemProperties");
            try {
                Method get = systemProperties.getMethod("get",
                   String.class, String.class);
                if (get == null) {
                    return "WTF?!";
                }
                try {
                    final String value = (String)get.invoke(
                        systemProperties, SELECT_RUNTIME_PROPERTY,
                        /* Assuming default is */"Dalvik");
                    if (LIB_DALVIK.equals(value)) {
                        return "Dalvik";
                    } else if (LIB_ART.equals(value)) {
                        return "ART";
                    } else if (LIB_ART_D.equals(value)) {
                        return "ART debug build";
                    }

                    return value;
                } catch (IllegalAccessException e) {
                    return "IllegalAccessException";
                } catch (IllegalArgumentException e) {
                    return "IllegalArgumentException";
                } catch (InvocationTargetException e) {
                    return "InvocationTargetException";
                }
            } catch (NoSuchMethodException e) {
                return "SystemProperties.get(String key, String def) method is not found";
            }
        } catch (ClassNotFoundException e) {
            return "SystemProperties class is not found";
        }
    }
}
```

DalvikとARTとART debg buildという3つの状態が取得できるのは面白いが，ART debg buildが何なのか分からないのとリフレクションを使っているので柔軟性は低い。

`System.getProperty("java.vm.version")`した値が2.0.0以上かを判定する方法がシンプルで良さそう。