LoginSignup
1
0

More than 3 years have passed since last update.

adbでAndroidのIMEIを取得する方法が大体間違っている件

Posted at

TL;DR

以下のように、 iphonesubinfo サービスに 4 を渡すのがAndroid 8以降は正しい。

adb shell service call iphonesubinfo 4

よくある以下のやり方は、 API Level 30(Android 11相当)以降で deprecated指定されるので注意が必要

adb shell service call iphonesubinfo 1

以下、ちょっとだけ詳しい解説をします。

そもそも一体何をしているのか

もう一度コードを貼ります。

adb shell service call iphonesubinfo 4

このうち adb shell についてはadbのshellにコマンドを引き渡すための記述に過ぎないので、実際にAndroidの中で実行されているのは service call iphonesubinfo 4 となります。

service とはなにか

service はAndroid上で動作しているサービスの操作を行うものです。Linuxにも同盟のコマンドはありますが、それとは似て非なるものと言えます。

-h で表示される文言は以下のようなものです。

Usage: service [-h|-?]
       service list
       service check SERVICE
       service call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR | null | fd f | nfd n | afd f ] ...
Options:
   i32: Write the 32-bit integer N into the send parcel.
   i64: Write the 64-bit integer N into the send parcel.
   f:   Write the 32-bit single-precision number N into the send parcel.
   d:   Write the 64-bit double-precision number N into the send parcel.
   s16: Write the UTF-16 string STR into the send parcel.
  null: Write a null binder into the send parcel.
    fd: Write a file descriptor for the file f to the send parcel.
   nfd: Write file descriptor n to the send parcel.
   afd: Write an ashmem file descriptor for a region containing the data from file f to the send parcel.

上記を見ると、service call iphonesubinfo 4 の意味が見えてきます。
call はサービスの呼び出しを行い、iphonesubinfo はサービス名、 4 はコードというわけですね。

Android上で動作するサービス

Android上で動作するサービスの取得方法も先程のヘルプを見ればなんとなくわかりますね。そう、 service list です。やってみましょう。

Found 199 services:
0   DockObserver: []
1   SurfaceFlinger: [android.ui.ISurfaceComposer]
2   accessibility: [android.view.accessibility.IAccessibilityManager]
3   account: [android.accounts.IAccountManager]
4   activity: [android.app.IActivityManager]
5   activity_task: [android.app.IActivityTaskManager]
6   adb: [android.debug.IAdbManager]
7   alarm: [android.app.IAlarmManager]
8   android.hardware.identity.IIdentityCredentialStore/default: []
9   android.hardware.power.IPower/default: []
10  android.hardware.rebootescrow.IRebootEscrow/default: []
(以下省略)

ここで注目すべきは、多くのサービスがサービス名と合わせて、 android.view.accessibility.IAccessibilityManager のようなインタフェース名を公開していることです。実はこれらのファイルの多くはAndroidのソースコードに含まれており、例えば IAccesibilityManager は以下になります。

拡張子 *.aidl はAndroidのインタフェース定義言語(AIDL)を表し、名前通りそのAndroidサービスとやり取りする際のインターフェースが記述されています(記述はすごくJavaっぽいです)

Androidサービスの呼び出し方

同様に iphonesubinfo を探すと、以下のコードが見つかります。

中身は以下のような感じです。

interface IPhoneSubInfo {
    /** @deprecated Use {@link #getDeviceIdWithFeature(String, String) instead */
    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
    String getDeviceId(String callingPackage);
    /**
     * Retrieves the unique device ID, e.g., IMEI for GSM phones.
     */
    String getDeviceIdWithFeature(String callingPackage, String callingFeatureId);
     /**
     * Retrieves the unique Network Access ID
     */
    String getNaiForSubscriber(int subId, String callingPackage, String callingFeatureId);
    /**
     * Retrieves the unique device ID of a phone for the device, e.g., IMEI
     * for GSM phones.
     */
    String getDeviceIdForPhone(int phoneId, String callingPackage, String callingFeatureId);
    /**
     * Retrieves the IMEI.
     */
    String getImeiForSubscriber(int subId, String callingPackage, String callingFeatureId);
    /**
     * Retrieves the software version number for the device, e.g., IMEI/SV
     * for GSM phones.
     */
    String getDeviceSvn(String callingPackage, String callingFeatureId);

    //(中略)
}

ではこのAIDLのメソッドを呼び出すに当たり、最後の 4 という数字は何を意味するのか。
実はこの数字は、AIDLに定義されているメソッドの順番(1スタート)になっています。

したがって、 service call iphonesubinfo 4 で呼び出されるメソッドは、 getImeiForSubscriber となります。そう、これがIMEIを取得するメソッドだったのです。

では service call iphonesubinfo 1 で呼び出されるメソッドは、当然戦闘のメソッドですが、それは以下のようなアノテーションが付されています。

    /** @deprecated Use {@link #getDeviceIdWithFeature(String, String) instead */
    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
    String getDeviceId(String callingPackage);

そう、30以降ではサポートされず、別のメソッドを使うように、と書いてあります。

ただ、そもそもその「別のメソッド」というのが getImeiForSubscriber じゃないじゃないか、という話もあると思います。使うべきとされているのはこちらのメソッドです。

    /**
     * Retrieves the unique device ID, e.g., IMEI for GSM phones.
     */
    String getDeviceIdWithFeature(String callingPackage, String callingFeatureId);

はい、記述どおり、このメソッドはIMEIを返すことを保証しないのです。それは実は内部の実装依存です。したがってIMEIと明確に指定して抜き出したい場合には、 getImeiForSubscriber を、すなわち service call iphonesubinfo 4 をコールするのが望ましい、という話になります。

おつかれさまでした。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0