Android hạn chế truy cập định danh phần cứng (IMEI, Serial) từ Android 10 (API 29) do quyền riêng tư. Cần hiểu rõ các identifier, quyền cần thiết, và phương án thay thế.
Các loại Device Identifier
| Identifier | Mô tả | Quyền | Android 10+ | Đặc điểm |
|---|---|---|---|---|
| IMEI/MEID | Số định danh thiết bị di động (SIM slot) | READ_PHONE_STATE | Chỉ system app / carrier app | Duy nhất, không đổi, cần SIM |
| Serial Number | Số serial phần cứng | READ_PHONE_STATE | Chỉ system app | Duy nhất, không đổi |
| Android ID (SSAID) | ID ngẫu nhiên per app-signing-key + user | Không cần | Được phép (scoped per app) | Reset khi factory reset, khác nhau giữa app |
| Advertising ID (GAID) | ID quảng cáo, user có thể reset | Không cần (Google Play Services) | Được phép | Dùng cho ads/analytics, user opt-out |
| Instance ID / Firebase Installation ID | ID cài đặt app instance | Không cần | Được phép | Reset khi reinstall app |
| UUID tự sinh | Tạo random UUID, lưu SharedPreferences | Không cần | Được phép | Duy nhất per install, dễ triển khai |
Cách 1: Android ID (Khuyến nghị cho hầu hết trường hợp)
Không cần permission, hoạt động mọi phiên bản Android.
import android.provider.Settings.Secure;
public String getAndroidId(Context context) {
return Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
}
// Kết quả: "a1b2c3d4e5f6g7h8" (hex string 64-bit)
// Lưu ý: Trên emulator có thể trả về null hoặc giá trị cố định
Cách 2: UUID tự sinh (Best practice cho tracking user/install)
import android.content.Context;
import android.content.SharedPreferences;
import java.util.UUID;
public class DeviceIdManager {
private static final String PREFS_NAME = "device_id_prefs";
private static final String KEY_DEVICE_ID = "device_id";
private final SharedPreferences prefs;
public DeviceIdManager(Context context) {
prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
}
public String getDeviceId() {
String deviceId = prefs.getString(KEY_DEVICE_ID, null);
if (deviceId == null) {
deviceId = UUID.randomUUID().toString();
prefs.edit().putString(KEY_DEVICE_ID, deviceId).apply();
}
return deviceId;
}
public void resetDeviceId() {
prefs.edit().remove(KEY_DEVICE_ID).apply();
}
}
Cách 3: IMEI (Chỉ cho system/carrier app, Android < 10)
Deprecated/Restricted từ API 29. Chỉ để maintain legacy code.
// AndroidManifest.xml// Java (Legacy) TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { // API 26+ (Oreo) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { String imei = telephonyManager.getImei(); // Slot 1 // String imei2 = telephonyManager.getImei(1); // Slot 2 } else { // Deprecated String imei = telephonyManager.getDeviceId(); } }
Cách 4: Advertising ID (Google Play Services)
Dùng cho quảng cáo, analytics. Cần Google Play Services.
// build.gradle
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
// Java (Async - phải chạy background thread)
import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException;
import java.io.IOException;
public void fetchAdId(Context context) {
new Thread(() -> {
try {
AdvertisingIdClient.Info adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context);
String adId = adInfo.getId();
boolean limitAdTracking = adInfo.isLimitAdTrackingEnabled();
// Gửi adId lên server/main thread
} catch (GooglePlayServicesNotAvailableException |
GooglePlayServicesRepairableException | IOException e) {
e.printStackTrace();
}
}).start();
}
Cách 5: Firebase Installation ID (FID)
Dùng cho Firebase services, không cần permission.
// build.gradle
implementation 'com.google.firebase:firebase-installations:17.1.4'
// Java
FirebaseInstallations.getInstance().getId()
.addOnSuccessListener(fid -> {
// fid: "abc123..." - duy nhất cho app instance này
})
.addOnFailureListener(e -> { /* handle error */ });
So sánh & Khuyến nghị
| Use Case | Identifier khuyến nghị | Lý do |
|---|---|---|
| Analytics, tracking user behavior | Firebase Installation ID / UUID tự sinh | Không cần permission, ổn định per install |
| Licensing, anti-fraud, device binding | Android ID + UUID backup | Android ID ổn định per app+user, UUID fallback |
| Advertising, marketing | Advertising ID (GAID) | Tuân thủ policy, user có thể reset/opt-out |
| Banking, high-security (carrier partnership) | IMEI (chỉ system app) | Yêu cầu đặc quyền, không dùng cho app thông thường |
| Push notification targeting | FCM Token / FID | Tích hợp sẵn Firebase |
Ví dụ tổng hợp: Lấy ID phù hợp nhất
public class DeviceIdentifier {
public static String getBestDeviceId(Context context) {
// 1. Thử Firebase Installation ID (nếu dùng Firebase)
// 2. Fallback: UUID đã lưu
// 3. Fallback: Android ID
SharedPreferences prefs = context.getSharedPreferences("device_ids", Context.MODE_PRIVATE);
String uuid = prefs.getString("uuid", null);
if (uuid == null) {
uuid = UUID.randomUUID().toString();
prefs.edit().putString("uuid", uuid).apply();
}
return uuid;
}
public static String getAndroidId(Context context) {
return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
}
}
Chạy ứng dụng (Demo IMEI - Legacy)
App demo cũ: Yêu cầu runtime permission READ_PHONE_STATE → User allow → Hiển thị IMEI (hoặc "Permission denied" trên Android 10+).
Lưu ý quan trọng
- Không dùng IMEI/Serial cho app thông thường - Google Play sẽ reject từ API 29+
- Android ID có thể reset khi factory reset, khác nhau giữa user/profile/work profile
- UUID tự sinh mất khi clear data/uninstall - phù hợp cho "install ID"
- Advertising ID cần Google Play Services, user có thể bật "Opt out of Ads Personalization"
- Tuân thủ Best practices for unique identifiers