web-dev-qa-db-ja.com

Androidデバイスのシリアル番号を見つける方法

Androidアプリに一意のIDを使用する必要があり、デバイスのシリアル番号が適切な候補になると考えました。アプリでAndroidデバイスのシリアル番号を取得するにはどうすればよいですか?

113
Eno
TelephonyManager tManager = (TelephonyManager)myActivity.getSystemService(Context.TELEPHONY_SERVICE);
String uid = tManager.getDeviceId();

getSystemServiceは、Activityクラスのメソッドです。 getDeviceID()は、電話機が使用する無線(GSMまたはCDMA)に応じて、デバイスのMDNまたはMEIDを返します。

各デバイスは、ここで一意の値を返す必要があります(電話であると仮定)。これは、simスロットまたはCDMA無線を備えたAndroidデバイスで機能するはずです。あなたはそのAndroid駆動の電子レンジを独力で使用しています;-)

102
haseman

Dave Webbが言及しているように、 Android開発者ブログには記事があります これについて説明しています。

Googleの誰かと話をして、いくつかの項目についてさらに説明を加えました。前述のブログ投稿では言及されていないことを発見しました。

  • Android_IDが推奨されるソリューションです。 Android_IDは、Android <= 2.1または> = 2.3のバージョンで完全に信頼できます。投稿で言及されている問題があるのは2.2だけです。
  • いくつかのメーカーのいくつかのデバイスは、2.2のAndroid_IDバグの影響を受けます。
  • 私が判断できた限り、影響を受けるすべてのデバイスには 同じAndroid_ID 、つまり 9774d56d682e549c があります。これは、エミュレータが報告するのと同じデバイスIDです。
  • Googleは、OEMが多くまたはほとんどのデバイスの問題にパッチを適用したと考えていますが、2011年4月の初めから、少なくとも、Android_IDが壊れているデバイスを見つけるのは非常に簡単であることを確認できました。

Googleの推奨に基づいて、必要に応じてAndroid_IDをシードとして使用し、必要に応じてTelephonyManager.getDeviceId()にフォールバックし、失敗した場合はランダムに生成された一意のUUIDに頼って、各デバイスに一意のUUIDを生成するクラスを実装しましたアプリの再起動後も保持されます(ただし、アプリの再インストールは保持されません)。

import Android.content.Context;
import Android.content.SharedPreferences;
import Android.provider.Settings.Secure;
import Android.telephony.TelephonyManager;

import Java.io.UnsupportedEncodingException;
import Java.util.UUID;

public class DeviceUuidFactory {

    protected static final String PREFS_FILE = "device_id.xml";
    protected static final String PREFS_DEVICE_ID = "device_id";
    protected static volatile UUID uuid;

    public DeviceUuidFactory(Context context) {
        if (uuid == null) {
            synchronized (DeviceUuidFactory.class) {
                if (uuid == null) {
                    final SharedPreferences prefs = context
                            .getSharedPreferences(PREFS_FILE, 0);
                    final String id = prefs.getString(PREFS_DEVICE_ID, null);
                    if (id != null) {
                        // Use the ids previously computed and stored in the
                        // prefs file
                        uuid = UUID.fromString(id);
                    } else {
                        final String androidId = Secure.getString(
                            context.getContentResolver(), Secure.Android_ID);
                        // Use the Android ID unless it's broken, in which case
                        // fallback on deviceId,
                        // unless it's not available, then fallback on a random
                        // number which we store to a prefs file
                        try {
                            if (!"9774d56d682e549c".equals(androidId)) {
                                uuid = UUID.nameUUIDFromBytes(androidId
                                        .getBytes("utf8"));
                            } else {
                                final String deviceId = ((TelephonyManager) 
                                        context.getSystemService(
                                            Context.TELEPHONY_SERVICE))
                                            .getDeviceId();
                                uuid = deviceId != null ? UUID
                                        .nameUUIDFromBytes(deviceId
                                                .getBytes("utf8")) : UUID
                                        .randomUUID();
                            }
                        } catch (UnsupportedEncodingException e) {
                            throw new RuntimeException(e);
                        }
                        // Write the value out to the prefs file
                        prefs.edit()
                                .putString(PREFS_DEVICE_ID, uuid.toString())
                                .commit();
                    }
                }
            }
        }
    }

    /**
     * Returns a unique UUID for the current Android device. As with all UUIDs,
     * this unique ID is "very highly likely" to be unique across all Android
     * devices. Much more so than Android_ID is.
     * 
     * The UUID is generated by using Android_ID as the base key if appropriate,
     * falling back on TelephonyManager.getDeviceID() if Android_ID is known to
     * be incorrect, and finally falling back on a random UUID that's persisted
     * to SharedPreferences if getDeviceID() does not return a usable value.
     * 
     * In some rare circumstances, this ID may change. In particular, if the
     * device is factory reset a new device ID may be generated. In addition, if
     * a user upgrades their phone from certain buggy implementations of Android
     * 2.2 to a newer, non-buggy version of Android, the device ID may change.
     * Or, if a user uninstalls your app on a device that has neither a proper
     * Android ID nor a Device ID, this ID may change on reinstallation.
     * 
     * Note that if the code falls back on using TelephonyManager.getDeviceId(),
     * the resulting ID will NOT change after a factory reset. Something to be
     * aware of.
     * 
     * Works around a bug in Android 2.2 for many devices when using Android_ID
     * directly.
     * 
     * @see http://code.google.com/p/Android/issues/detail?id=10603
     * 
     * @return a UUID that may be used to uniquely identify your device for most
     *         purposes.
     */
    public UUID getDeviceUuid() {
        return uuid;
    }
}
71
emmby
String serial = null; 

try {
    Class<?> c = Class.forName("Android.os.SystemProperties");
    Method get = c.getMethod("get", String.class);
    serial = (String) get.invoke(c, "ro.serialno");
} catch (Exception ignored) {
}

このコードは、非表示のAndroid AP​​Iを使用してデバイスのシリアル番号を返します。

32
Roman SL
String deviceId = Settings.System.getString(getContentResolver(),
                                Settings.System.Android_ID);

ただし、Android IDが一意の識別子であるとは限りません。

16

これについて議論しているAndroid Developer's Blogの優れた投稿 があります。

TelephonyManager.getDeviceId() を使用しないことをお勧めします。タブレットなどの電話ではないAndroidデバイスでは機能せず、 READ_PHONE_STATE 権限が必要です。すべての電話で確実に動作するわけではありません。

代わりに、次のいずれかを使用できます。

  • Macアドレス
  • シリアルナンバー
  • Android_ID

投稿では、それぞれの長所と短所について説明しているので、読んでみる価値があります。

14
Dave Webb

デバイスに固有で、そのライフタイムが一定である単純な番号(工場出荷時のリセットまたはハッキングを除く)の場合は、 Settings.Secure.Android_ID を使用します。

String id = Secure.getString(getContentResolver(), Secure.Android_ID);

デバイスのシリアル番号(「システム設定/情報/ステータス」に表示されている番号)を使用できる場合は、Android IDにフォールバックします:

String serialNumber = Build.SERIAL != Build.UNKNOWN ? Build.SERIAL : Secure.getString(getContentResolver(), Secure.Android_ID);
12
Edward Brey

IMEIは優れていますが、Android電話付きのデバイスでのみ機能します。タブレットやその他のAndroidデバイスのサポートも検討する必要があります。これには電話はありません。

次のような代替手段があります:ビルドクラスメンバー、BT MAC、WLAN MAC、またはそれ以上-これらすべての組み合わせ。

私のブログの記事でこれらの詳細を説明しました: http://www.pocketmagic.net/?p=1662

7
radhoo

ここでの答えは、システムの更新を通じて永続的であり、すべてのデバイスに存在する完全なフェイルプルーフIDに言及していない(主にGoogleの個別のソリューションがないという事実による)なので、利用可能な2つの識別子と、実行時にそれらの間で選択するチェックを組み合わせて、次に最適なメソッドを投稿することにしました。

コードの前に、3つの事実:

  1. TelephonyManager.getDeviceId()(akaIMEI)は非GSM、3G、LTEなどのデバイスではうまく機能しないか、まったく機能しませんが、関連するハードウェアが存在する場合は常に一意のIDを返します(SIMがない場合でも) SIMスロットが存在しない場合でも挿入されます(一部のOEMはこれを行っています)。

  2. Gingerbread(Android 2.3)Android.os.Build.SERIALIMEIを提供しないデバイスに存在する必要がありますなので、Androidポリシーに従って、前述のハードウェアが存在しません。

  3. 事実(2.)により、これらの2つの一意の識別子の少なくとも1つは常に存在する、およびシリアルcanはIMEIと同時に存在します。

注:ファクト(1.)および(2.)は Googleの声明に基づく

ソリューション

上記の事実により、IMEIにバインドされたハードウェアがあるかどうかを確認することで常に一意の識別子を持ち、存在しない場合はSERIALにフォールバックできます。既存のSERIALが有効かどうかを確認できないためです。次の静的クラスは、そのような存在を確認し、IMEIまたはSERIALを使用するための2つのメソッドを提供します。

import Java.lang.reflect.Method;

import Android.content.Context;
import Android.content.pm.PackageManager;
import Android.os.Build;
import Android.provider.Settings;
import Android.telephony.TelephonyManager;
import Android.util.Log;

public class IDManagement {

    public static String getCleartextID_SIMCHECK (Context mContext){
        String ret = "";

        TelephonyManager telMgr = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);

        if(isSIMAvailable(mContext,telMgr)){
            Log.i("DEVICE UNIQUE IDENTIFIER",telMgr.getDeviceId());
            return telMgr.getDeviceId();

        }
        else{
            Log.i("DEVICE UNIQUE IDENTIFIER", Settings.Secure.Android_ID);

//          return Settings.Secure.Android_ID;
            return Android.os.Build.SERIAL;
        }
    }


    public static String getCleartextID_HARDCHECK (Context mContext){
        String ret = "";

        TelephonyManager telMgr = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
        if(telMgr != null && hasTelephony(mContext)){           
            Log.i("DEVICE UNIQUE IDENTIFIER",telMgr.getDeviceId() + "");

            return telMgr.getDeviceId();    
        }
        else{
            Log.i("DEVICE UNIQUE IDENTIFIER", Settings.Secure.Android_ID);

//          return Settings.Secure.Android_ID;
            return Android.os.Build.SERIAL;
        }
    }


    public static boolean isSIMAvailable(Context mContext, 
            TelephonyManager telMgr){

        int simState = telMgr.getSimState();

        switch (simState) {
        case TelephonyManager.SIM_STATE_ABSENT:
            return false;
        case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
            return false;
        case TelephonyManager.SIM_STATE_PIN_REQUIRED:
            return false;
        case TelephonyManager.SIM_STATE_PUK_REQUIRED:
            return false;
        case TelephonyManager.SIM_STATE_READY:
            return true;
        case TelephonyManager.SIM_STATE_UNKNOWN:
            return false;
        default:
            return false;
        }
    }

    static public boolean hasTelephony(Context mContext)
    {
        TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
        if (tm == null)
            return false;

        //devices below are phones only
        if (Build.VERSION.SDK_INT < 5)
            return true;

        PackageManager pm = mContext.getPackageManager();

        if (pm == null)
            return false;

        boolean retval = false;
        try
        {
            Class<?> [] parameters = new Class[1];
            parameters[0] = String.class;
            Method method = pm.getClass().getMethod("hasSystemFeature", parameters);
            Object [] parm = new Object[1];
            parm[0] = "Android.hardware.telephony";
            Object retValue = method.invoke(pm, parm);
            if (retValue instanceof Boolean)
                retval = ((Boolean) retValue).booleanValue();
            else
                retval = false;
        }
        catch (Exception e)
        {
            retval = false;
        }

        return retval;
    }


}

getCleartextID_HARDCHECKの使用についてアドバイスします。リフレクションが環境に貼り付いていない場合は、代わりにgetCleartextID_SIMCHECKメソッドを使用しますが、特定のSIMプレゼンスのニーズに合わせて調整する必要があることを考慮してください。

PS:OEMにはGoogleポリシーに対してSERIALをバグ管理するために管理されている(同じシリアルの複数のデバイス)があり、大規模なOEM(開示されておらず、どちらのブランドなのかわかりません。Samsungのようです)。

免責事項:これは一意のデバイスIDを取得するという元の質問に答えますが、OPはAPPに一意のIDが必要であると述べることであいまいさを導入しました。このようなシナリオの場合、Android_IDの方が優れている場合でも、たとえば、2つの異なるROMインストール(同じROMであってもよい)を介したアプリのTitanium Backupの後は機能しません。私のソリューションは、フラッシュや工場出荷時のリセットに依存しない永続性を維持し、IMEIまたはSERIALの改ざんがハッキング/ハードウェアの改造によって発生した場合にのみ失敗します。

6
leRobot

上記のすべてのアプローチには問題があります。 Google i/oでは、Reto Meierがこれにアプローチする方法に対する堅牢な回答をリリースしました。これは、インストール全体でユーザーを追跡するほとんどの開発者のニーズを満たすはずです。

このアプローチにより、匿名の安全なユーザーIDが得られます。このユーザーIDは、さまざまなデバイス(プライマリGoogleアカウントに基づくタブレットを含む)および同じデバイスのインストール間でユーザーに対して永続的です。基本的なアプローチは、ランダムなユーザーIDを生成し、これをアプリの共有設定に保存することです。次に、Googleのバックアップエージェントを使用して、Googleアカウントにリンクされた共有設定をクラウドに保存します。

完全なアプローチを行ってみましょう。最初に、Android Backup Serviceを使用してSharedPreferencesのバックアップを作成する必要があります。次のリンクからアプリを登録してください: http://developer.Android.com/google/backup/signup.html

Googleは、マニフェストに追加する必要があるバックアップサービスキーを提供します。また、次のようにBackupAgentを使用するようにアプリケーションに指示する必要があります。

<application Android:label="MyApplication"
         Android:backupAgent="MyBackupAgent">
    ...
    <meta-data Android:name="com.google.Android.backup.api_key"
        Android:value="your_backup_service_key" />
</application>

次に、バックアップエージェントを作成し、共有設定にヘルパーエージェントを使用するように指示する必要があります。

public class MyBackupAgent extends BackupAgentHelper {
    // The name of the SharedPreferences file
    static final String PREFS = "user_preferences";

    // A key to uniquely identify the set of backup data
    static final String PREFS_BACKUP_KEY = "prefs";

    // Allocate a helper and add it to the backup agent
    @Override
    public void onCreate() {
        SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this,          PREFS);
        addHelper(PREFS_BACKUP_KEY, helper);
    }
}

バックアップを完了するには、メインアクティビティでBackupManagerのインスタンスを作成する必要があります。

BackupManager backupManager = new BackupManager(context);

最後に、ユーザーIDがまだ存在しない場合は作成し、SharedPreferencesに保存します。

  public static String getUserID(Context context) {
            private static String uniqueID = null;
        private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID";
    if (uniqueID == null) {
        SharedPreferences sharedPrefs = context.getSharedPreferences(
                MyBackupAgent.PREFS, Context.MODE_PRIVATE);
        uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
        if (uniqueID == null) {
            uniqueID = UUID.randomUUID().toString();
            Editor editor = sharedPrefs.edit();
            editor.putString(PREF_UNIQUE_ID, uniqueID);
            editor.commit();

            //backup the changes
            BackupManager mBackupManager = new BackupManager(context);
            mBackupManager.dataChanged();
        }
    }

    return uniqueID;
}

このUser_IDは、ユーザーがデバイスを切り替えた場合でも、インストール全体で保持されます。

このアプローチの詳細については、Retoの講演をご覧ください http://www.google.com/events/io/2011/sessions/Android-protips-advanced-topics-for-expert-Android-app-developers。 html

バックアップエージェントの実装方法の詳細については、こちらの開発者向けサイトをご覧ください。 http://developer.Android.com/guide/topics/data/backup.html のセクションを特にお勧めしますバックアップは瞬時に行われないため、テストの最後になります。したがって、テストするにはバックアップを強制する必要があります。

5
TechnoTony

もう1つの方法は、許可なしでアプリで/ sys/class/Android_usb/Android0/iSerialを使用することです。

user@creep:~$ adb Shell ls -l /sys/class/Android_usb/Android0/iSerial
-rw-r--r-- root     root         4096 2013-01-10 21:08 iSerial
user@creep:~$ adb Shell cat /sys/class/Android_usb/Android0/iSerial
0A3CXXXXXXXXXX5

Javaでこれを行うには、FileInputStreamを使用してiSerialファイルを開き、文字を読み取ります。すべてのデバイスにこのファイルがあるわけではないため、必ず例外ハンドラでラップしてください。

少なくとも次のデバイスは、このファイルが誰でも読み取り可能なことがわかっています。

  • Galaxy Nexus
  • ネクサスS
  • Motorola Xoom 3g
  • 東芝AT300
  • HTC One V
  • ミニMK802
  • Samsung Galaxy S II

こちらのブログ記事もご覧ください: http://insitusec.blogspot.com/2013/01/leaking-Android-hardware-serial-number.html ここで、他のどのファイルが情報に利用できるかについて議論します。

2
insitusec

文字列としてのAndroid OSデバイスの一意のデバイスID。

String deviceId;
    final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        if (mTelephony.getDeviceId() != null){
            deviceId = mTelephony.getDeviceId(); 
         }
        else{
            deviceId = Secure.getString(getApplicationContext().getContentResolver(),   Secure.Android_ID); 
         }

ただし、Googleが提案する次の方法を強くお勧めします。

アプリのインストールの識別

1
Elenasys

Build.SERIALは最も簡単な方法ですが、完全に信頼できるわけではありませんが、 空にすることができます または場合によっては異なる値を返します(- proof 1proof 2 )あなたのデバイスの設定で見ることができるものより。

デバイスの製造元とAndroidバージョンに応じてその数を取得する方法はいくつかあるため、単一の Gist で見つかったすべての可能なソリューションをコンパイルすることにしました。以下はその簡略版です。

public static String getSerialNumber() {
    String serialNumber;

    try {
        Class<?> c = Class.forName("Android.os.SystemProperties");
        Method get = c.getMethod("get", String.class);

        serialNumber = (String) get.invoke(c, "gsm.sn1");
        if (serialNumber.equals(""))
            serialNumber = (String) get.invoke(c, "ril.serialnumber");
        if (serialNumber.equals(""))
            serialNumber = (String) get.invoke(c, "ro.serialno");
        if (serialNumber.equals(""))
            serialNumber = (String) get.invoke(c, "sys.serialnumber");
        if (serialNumber.equals(""))
            serialNumber = Build.SERIAL;

        // If none of the methods above worked
        if (serialNumber.equals(""))
            serialNumber = null;
    } catch (Exception e) {
        e.printStackTrace();
        serialNumber = null;
    }

    return serialNumber;
}
1
flawyte

@hasermanが言うように:

TelephonyManager tManager = (TelephonyManager)myActivity.getSystemService(Context.TELEPHONY_SERVICE);
String uid = tManager.getDeviceId();

ただし、マニフェストファイルに許可を含める必要があります。

<uses-permission Android:name="Android.permission.READ_PHONE_STATE"/>
1
cesards

私はこの質問が古いことを知っていますが、1行のコードで行うことができます

String deviceID = Build.SERIAL;

0
MichaelStoddart