在Android開發(fā)者官方blog上已經(jīng)有一篇文章對此做了總結(jié)(參考鏈接1), 這里結(jié)合自已查詢的資料再總結(jié)一下, 并給出最終符合要求的解決方案。
1. ANDROID_ID, Secure.ANDROID_ID
但是據(jù)說Motorola的Droid2犯了個低級錯誤, 所有的Droid2上使用同一個ANDROID_ID, 即9774D56D682E549C, ANDROID_ID的內(nèi)部實(shí)現(xiàn)是存儲在系統(tǒng)的一個SQLite數(shù)據(jù)庫里, root過就有權(quán)限修改了
2. TelephonyManager.getDeviceId()
返回IMEI, MEID 或 ESN, 需要READ_PHONE_STATE權(quán)限
在過去, 那時所有的Android設(shè)備都是電話, 這個方法足夠用了, 但是現(xiàn)在連Android電視也有了
3. Serial Number, android.os.Build.SERIAL
在Android 2.3以后的版本, 非手機(jī)的設(shè)備必須設(shè)置此序列號, 有些手機(jī)也設(shè)置此序列號
4. Mac Address
WiFi或Bluetooth設(shè)備的網(wǎng)卡物理地址, 但是并不是所有Android設(shè)備都有WiFi或Bluetooth, 且在WiFi沒打開時也不能正常獲取
5. cpu serial
CPU序列號, 通過cat /proc/cpuinfo可以查詢, 我的Sumsung能正確查詢, 但在有的設(shè)備上查詢出的序列號是0
我的實(shí)現(xiàn)是優(yōu)先使用ANDROID_ID, 再次是imei, 最后是android.os.Build.SERIAL
在java層的實(shí)現(xiàn)如下(java代碼):
{% highlight java %} String deviceId; String androidId = Secure.getString(getBaseContext().getContentResolver(), Secure.ANDROID_ID); String imei = ((TelephonyManager) getBaseContext().getSystemService(Context.TELEPHONY_SERVICE )).getDeviceId(); String serial = android.os.Build.SERIAL;
deviceId = androidId; if (deviceId == null || "9774d56d682e549c".equals(deviceId)) { deviceId = imei; if (deviceId == null || "".equals(deviceId)) { deviceId = serial; if (deviceId == null || deviceId.equals("") || deviceId.equals("unknown")) { deviceId = null; } } }
Log.d(TAG, "ANDROID-ID: " + androidId); Log.d(TAG, "imei: " + imei); Log.d(TAG, "SERIAL: " + serial); Log.d(TAG, "final deviceId: " + deviceId); {% endhighlight %}
在NDK層的實(shí)現(xiàn)就比較困難了, 脫離了java層的Context對象, 就無法獲取ANDROID_ID和DeviceID了, 這里我增加了一個接口參數(shù), 在調(diào)用時傳入一個Context對象進(jìn)來
在NDK層的實(shí)現(xiàn)如下(c代碼):
{% highlight c %} int get_android_device_id(char device_id[64], JNIEnv *jenv, jobject jcontext) { static char _device_id[64]; const char *android_id, *imei, *serial; jstring jandroid_id, jimei, jserial;
if (!jenv || !jcontext) {
goto end;
}
/* String androidId = android.provider.Settings.Secure.getString(getApplicationContext().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID); */
/* String imei = ((android.telephony.TelephonyManager) getApplicationContext().getSystemService(android.content.Context.TELEPHONY_SERVICE)).getDeviceId(); */
/* String serial = android.os.Build.SERIAL; */
jandroid_id = (*jenv)->CallStaticObjectMethod(jenv,
(*jenv)->FindClass(jenv, "android/provider/Settings$Secure"),
(*jenv)->NewStringUTF(jenv, "android_id"));
jimei = (*jenv)->CallObjectMethod(jenv,
(*jenv)->CallObjectMethod(jenv, jcontext, (*jenv)->GetMethodID(jenv, (*jenv)->GetObjectClass(jenv, jcontext), "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"), (*jenv)->NewStringUTF(jenv, "phone")),
jimei = (*jenv)->GetMethodID(jenv, (*jenv)->FindClass(jenv, "android/telephony/TelephonyManager"), "getDeviceId", "()Ljava/lang/String;"));
jserial = (*jenv)->GetStaticObjectField(jenv,
(*jenv)->FindClass(jenv, "android/os/Build"),
(*jenv)->GetStaticFieldID(jenv, (*jenv)->FindClass(jenv, "android/os/Build"), "SERIAL", "Ljava/lang/String;"));
android_id = jandroid_id ? (*jenv)->GetStringUTFChars(jenv, jandroid_id, NULL) : NULL;
imei = jimei ? (*jenv)->GetStringUTFChars(jenv, jimei, NULL) : NULL;
serial = jserial ? (*jenv)->GetStringUTFChars(jenv, jserial, NULL) : NULL;
if (android_id && strcmp(android_id, "") && strcmp(android_id, "9774d56d682e549c")) {
strcpy(_device_id, android_id);
} else if (imei && strcmp(imei, "")) {
strcpy(_device_id, imei);
} else if (serial && strcmp(serial, "")) {
strcpy(_device_id, serial);
} else {
strcpy(_device_id, "");
}
if (strlen(_device_id) <= 10) {
strcpy(_device_id, "");
}
(*jenv)->ReleaseStringUTFChars(jenv, jandroid_id, android_id);
(*jenv)->ReleaseStringUTFChars(jenv, jimei, imei);
(*jenv)->ReleaseStringUTFChars(jenv, jserial, serial);
end: if (device_id) { strcpy(device_id, _device_id); }
return 0;
} {% endhighlight %}
JNI代碼寫起來很啰嗦, 寫這段代碼時對著手冊看了半天