How Android gets a valid DeviceId

insert image description here

foreword

Starting with Android 10, apps must have the READ_PRIVILEGED_PHONE_STATE privilege to access the device's non-resettable identifiers (including IMEI and serial number).

And this permission is a system permission, which means that general applications will no longer be able to obtain IMEI and serial numbers

Affected methods include:

  • Build

    • getSerial()
  • TelephonyManager

    • getImei()
    • getDeviceId()
    • getGirl()
    • getSimSerialNumber()
    • getSubscriberId()

If your app doesn't have that permission, but you still try to query for information about non-resettable identifiers, the platform's response varies depending on the target SDK version:

  1. A SecurityException will occur if the app targets Android 10 or higher.
  2. If the app targets Android 9 (API level 28) or lower, the method returns null or placeholder data (if the app has the READ_PHONE_STATE permission). Otherwise, a SecurityException occurs.

Google also gave a solution

Many usage scenarios do not require non-resettable device identifiers. For example, if your app uses non-resettable device identifiers for ad tracking or user profiling purposes, use Android Advertising ID for those specific use cases. To learn more, see Best Practices for Unique Identifiers.

Most of the solutions here are invalid for China, such as advertising ID, which requires the service of Google Play, but it is castrated on domestic mobile phones. So we can only refer to some available solutions.

Official Unique Identifier Advice

In this part, let's take a look at the official unique identification suggestions

Use Advertising ID

Don’t think about it in China, you need to rely on google play services

Using Instance IDs and GUIDs

It is only valid for a single application, and it will change after uninstalling, which is not advisable.

Do not use MAC address

The MAC address is globally unique, cannot be reset by the user, and does not change after a factory reset. Therefore, the use of MAC addresses for any form of user identification is generally not recommended. Devices running Android 10 (API level 29) and higher report a randomized MAC address for all apps that are not the device owner app.

In Android 6.0 (API level 23) to Android 9 (API level 28), it is not possible to use local device Mac addresses such as Wi-Fi and Bluetooth through third-party APIs.WifiInfo.getMacAddress()Methods andBluetoothAdapter.getDefaultAdapter().getAddress()Both methods return 02:00:00:00:00:00 .

Additionally, on Android 6.0 through Android 9, you must have the following permissions to access the MAC addresses of nearby external devices from Bluetooth and Wi-Fi scans:

method/property required permissions
WifiManager.getScanResults() ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION
BluetoothDevice.ACTION_FOUND ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION
BluetoothLeScanner.startScan(ScanCallback) ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION

Therefore, mac is a reliable identification second only to DeviceId, but it cannot be obtained after android 6.0. But there are other ways to improve, see below.

Identifier properties

a bunch of nonsense

Common Use Cases and Applicable Identifiers

It is also a bunch of nonsense, or it is not available in China, but SSAID is mentioned.

SSAID, namely ANDROID_ID (Settings.Secure.ANDROID_ID), ushered in changes in the 8.0 system, as follows:

For apps installed to a version of Android 8.0 (API level 26) prior to the OTA, the ANDROID_ID value will remain the same unless uninstalled and reinstalled after the OTA. To preserve values ​​during uninstall after OTA, developers can use key/value backups to associate old and new values.

For apps installed on devices running Android 8.0, the value of ANDROID_ID will now be scoped based on the app signing key and user. Each combination of app signing key, user, and device has a unique ANDROID_ID value. So apps running on the same device but with different signing keys will no longer see the same Android ID (even for the same user).

As long as the signing key is the same (and the app was not installed to a version of O prior to OTA), the value of ANDROID_ID will not change when the package is uninstalled or reinstalled.

Even if the package signing key changes due to a system update, the value of ANDROID_ID will not change.

It can be seen that after 8.0, ANDROID_ID is associated with the application signature, and applications with the same signature share the same ANDROID_ID, and uninstalling and reinstalling will not change.

Before 8.0, ANDROID_ID was associated with the device. When the device is started for the first time, the system will randomly generate a 64-bit number and save it in the mobile phone system in the form of a hexadecimal string. When the phone is restored to factory settings, Android ID will be reset, which is the main difference between Android ID and Device ID. Of course, there are other bugs, such as some manufacturers get null and so on.

Therefore, ANDROID_ID is one of the options that can be considered, which will be detailed later.

solution

It is impossible to obtain a stable DeviceId for one behavior. We need to combine multiple behaviors.

DeviceId

The first is the traditional DeviceId, which is still very stable after Android 10.

ANDROID_ID

After Android 8.0, you can consider using ANDROID_ID instead of DeviceId.

Settings.System.getString(BaseApp.getAppContext().getContentResolver(), Settings.Secure.ANDROID_ID);

In this way, a version judgment can be made. If it is lower than 10.0 (or 8.0), get DeviceId, otherwise get ANDROID_ID

Mac address

If the above two steps are still null, you can use the mac address, but the mac cannot be obtained through WifiInfo.getMacAddress() after 6.0, so we need to deal with it, the code is as follows:

public static String getMac(Context context) {
    
    
    String mac = "";
    if (context == null) {
    
    
        return mac;
    }
    if (Build.VERSION.SDK_INT < 23) {
    
    
        mac = getMacBySystemInterface(context);
    } else {
    
    
        mac = getMacByJavaAPI();
        if (TextUtils.isEmpty(mac)){
    
    
            mac = getMacBySystemInterface(context);
        }
    }
    return mac;

}

@TargetApi(9)
private static String getMacByJavaAPI() {
    
    
    try {
    
    
        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
        while (interfaces.hasMoreElements()) {
    
    
            NetworkInterface netInterface = interfaces.nextElement();
            if ("wlan0".equals(netInterface.getName()) || "eth0".equals(netInterface.getName())) {
    
    
                byte[] addr = netInterface.getHardwareAddress();
                if (addr == null || addr.length == 0) {
    
    
                    return null;
                }
                StringBuilder buf = new StringBuilder();
                for (byte b : addr) {
    
    
                    buf.append(String.format("%02X:", b));
                }
                if (buf.length() > 0) {
    
    
                    buf.deleteCharAt(buf.length() - 1);
                }
                return buf.toString().toLowerCase(Locale.getDefault());
            }
        }
    } catch (Throwable e) {
    
    
    }
    return null;
}

private static String getMacBySystemInterface(Context context) {
    
    
    if (context == null) {
    
    
        return "";
    }
    try {
    
    
        WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        if (checkPermission(context, Manifest.permission.ACCESS_WIFI_STATE)) {
    
    
            WifiInfo info = wifi.getConnectionInfo();
            return info.getMacAddress();
        } else {
    
    
            return "";
        }
    } catch (Throwable e) {
    
    
        return "";
    }
}

It can be seen that 6.0 is directly obtained under 23, otherwise it is obtained through NetworkInterface first, and if it cannot be obtained, it can be obtained through the original method.
At present, this step can still be obtained stably.

UUID

Out-of-the-box behavior. Because we need to generate it manually, and each generation is different.

UUID.randomUUID().toString()

So it must be generated once and saved. This has a problem. If you save it to the internal storage of the application, it must be regenerated after uninstalling and reinstalling, so that it cannot be judged that it is the same device.

So it is best to save it to an external storage to ensure that the last value can be read after uninstalling and reinstalling.

This is generally the most stable unless the file is manually deleted.

So the best solution is to combine the above four solutions together, one by one. At present, the guidance plans of various mobile phone manufacturers are only these few plans.

Replenish

In addition to the above solutions, there is also an SDK provided by the Mobile Security Alliance (led by ICT Institute), which can obtain several device identifiers, and most domestic manufacturers support it.

But you need to apply for it, and haven't tested it yet.

Summarize

From the above analysis, it can be seen that the official has indeed given many alternatives, but most of them cannot be used due to domestic restrictions. Therefore, in China, a unique id is basically obtained by sequentially obtaining DeviceId, ANDROID_ID, MAC, and UUID. The process is roughly as follows:

不存在
不存在
不存在
存在
存在
存在
DeviceId?
ANDROID_ID?
MAC?
生成UUID
返回

You may be interested:
Android 13 is released, let's take a look at the new features

A detailed interpretation of the event distribution mechanism in Android

Guess you like

Origin blog.csdn.net/chzphoenix/article/details/122845429