Androidアプリに一意のIDを使用する必要があり、デバイスのシリアル番号が適切な候補になると考えました。アプリでAndroidデバイスのシリアル番号を取得するにはどうすればよいですか?
TelephonyManager tManager = (TelephonyManager)myActivity.getSystemService(Context.TELEPHONY_SERVICE);
String uid = tManager.getDeviceId();
getSystemServiceは、Activityクラスのメソッドです。 getDeviceID()は、電話機が使用する無線(GSMまたはCDMA)に応じて、デバイスのMDNまたはMEIDを返します。
各デバイスは、ここで一意の値を返す必要があります(電話であると仮定)。これは、simスロットまたはCDMA無線を備えたAndroidデバイスで機能するはずです。あなたはそのAndroid駆動の電子レンジを独力で使用しています;-)
Dave Webbが言及しているように、 Android開発者ブログには記事があります これについて説明しています。
Googleの誰かと話をして、いくつかの項目についてさらに説明を加えました。前述のブログ投稿では言及されていないことを発見しました。
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;
}
}
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 APIを使用してデバイスのシリアル番号を返します。
String deviceId = Settings.System.getString(getContentResolver(),
Settings.System.Android_ID);
ただし、Android IDが一意の識別子であるとは限りません。
これについて議論しているAndroid Developer's Blogの優れた投稿 があります。
TelephonyManager.getDeviceId()
を使用しないことをお勧めします。タブレットなどの電話ではないAndroidデバイスでは機能せず、 READ_PHONE_STATE
権限が必要です。すべての電話で確実に動作するわけではありません。
代わりに、次のいずれかを使用できます。
投稿では、それぞれの長所と短所について説明しているので、読んでみる価値があります。
デバイスに固有で、そのライフタイムが一定である単純な番号(工場出荷時のリセットまたはハッキングを除く)の場合は、 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);
IMEIは優れていますが、Android電話付きのデバイスでのみ機能します。タブレットやその他のAndroidデバイスのサポートも検討する必要があります。これには電話はありません。
次のような代替手段があります:ビルドクラスメンバー、BT MAC、WLAN MAC、またはそれ以上-これらすべての組み合わせ。
私のブログの記事でこれらの詳細を説明しました: http://www.pocketmagic.net/?p=1662
ここでの答えは、システムの更新を通じて永続的であり、すべてのデバイスに存在する完全なフェイルプルーフIDに言及していない(主にGoogleの個別のソリューションがないという事実による)なので、利用可能な2つの識別子と、実行時にそれらの間で選択するチェックを組み合わせて、次に最適なメソッドを投稿することにしました。
コードの前に、3つの事実:
TelephonyManager.getDeviceId()
(akaIMEI)は非GSM、3G、LTEなどのデバイスではうまく機能しないか、まったく機能しませんが、関連するハードウェアが存在する場合は常に一意のIDを返します(SIMがない場合でも) SIMスロットが存在しない場合でも挿入されます(一部のOEMはこれを行っています)。
Gingerbread(Android 2.3)Android.os.Build.SERIAL
IMEIを提供しないデバイスに存在する必要がありますなので、Androidポリシーに従って、前述のハードウェアが存在しません。
事実(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の改ざんがハッキング/ハードウェアの改造によって発生した場合にのみ失敗します。
上記のすべてのアプローチには問題があります。 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 のセクションを特にお勧めしますバックアップは瞬時に行われないため、テストの最後になります。したがって、テストするにはバックアップを強制する必要があります。
もう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ファイルを開き、文字を読み取ります。すべてのデバイスにこのファイルがあるわけではないため、必ず例外ハンドラでラップしてください。
少なくとも次のデバイスは、このファイルが誰でも読み取り可能なことがわかっています。
こちらのブログ記事もご覧ください: http://insitusec.blogspot.com/2013/01/leaking-Android-hardware-serial-number.html ここで、他のどのファイルが情報に利用できるかについて議論します。
文字列としての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が提案する次の方法を強くお勧めします。
Build.SERIAL
は最も簡単な方法ですが、完全に信頼できるわけではありませんが、 空にすることができます または場合によっては異なる値を返します(- proof 1 、 proof 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;
}
@hasermanが言うように:
TelephonyManager tManager = (TelephonyManager)myActivity.getSystemService(Context.TELEPHONY_SERVICE);
String uid = tManager.getDeviceId();
ただし、マニフェストファイルに許可を含める必要があります。
<uses-permission Android:name="Android.permission.READ_PHONE_STATE"/>
私はこの質問が古いことを知っていますが、1行のコードで行うことができます
String deviceID = Build.SERIAL;