Unique identification of Android device (multiple implementation schemes)

Posted by Jnerocorp on Wed, 19 Jan 2022 18:59:37 +0100

preface

During project development, this kind of requirement will be encountered: obtain the unique device ID, which is used to:
1. Identify a unique equipment for accurate data distribution or data statistical analysis;
2. The account number is bound to the device;
3.....

analysis

For such articles, there are many materials on the Internet, such as using IMEI, MAC, etc. as equipment identification.
However, students who have read these articles or conducted in-depth research should be aware that these data have defects: some cannot be obtained because of permissions, some are repeated, and some cannot be obtained at all, that is to say, the only problem of the equipment cannot be solved perfectly.

So, what data can be used to represent the uniqueness of the device?

programme

Scheme 1: UUID + sharepreference (access)

When the APP is used for the first time, the UUID is created and saved to SharePreference. When you use it again in the future, you can directly take it out of SharePreference; Advantages: unique data without permission; Disadvantages: it will be deleted with the APP, that is, when the APP is reinstalled, the DeviceId value will change (New UUID);

Scheme 2: UUID + SD card (access)

When APP is used for the first time, UUID is created and saved to SD card. When you use it again in the future, you can take it out directly from the SD card; Many apps do this; Advantages: the data is unique and cannot be deleted with the APP; Disadvantages: SD card reading and writing permission is required; Users cannot be prevented from manually deleting SD card files;

Scenario 3: imei + android_id + serial + hardware uuid (self generated)

If you want to be unique and do not want to regenerate the UUID due to user deletion, what should you do?

Independent of random UUID, we create unique data according to hardware identification; We can splice multiple available hardware identifications (independent of authority as far as possible) to minimize repeatability. imei, Android_ For example, ID and serial, if values can be obtained, each data can almost represent a unique. If these data can be obtained, the repeatability of the combined data will be reduced to very low (UUID also has repeatability, but the repeatability is very low)

So, which hardware identification is appropriate?

AndroidId: for example: df176fbb152ddce, no permission is required. Very few devices can't get data or get wrong data;
serial: for example, LKX7N18328000931 does not require permission, and very few devices cannot obtain data;
IMEI: for example: 23b12e30ec8a2f17, permission is required;
Mac: for example: 6e:a5: Permission is required. The data obtained by the higher version mobile phone is 02:00 (not available)
Build.BOARD ^ such as BLA ^ name of main board, no authority is required, and it is the same as the equipment of the same model
Build.BRAND , such as HUAWEI , manufacturer's name, without permission, the same as the equipment of the same model
Build.HARDWARE name, such as kirin970, does not require permission and is the same as the equipment of the same model
Build...... More hardware information, omitted
 

After analyzing so many hardware identifiers, we use imei + android_id + serial + hardware UUID (generated using the Build attribute. If the hardware information remains unchanged, the UUID value remains unchanged). This is the actual scheme of our project. You can also freely combine the hardware identification according to your own needs.

Then, the problem comes again. The hardware identification length of different devices is different, and the DeviceId string length of splicing processing is different. How can we unify the length?

It's also very simple. Let's splice it first DeviceId Data, take it SHA1 Value, and then turn to hexadecimal (Unified 40 bit length)

Implementation:

import android.content.Context;
import android.os.Build;
import android.provider.Settings;
import android.telephony.TelephonyManager;

import java.security.MessageDigest;
import java.util.Locale;
import java.util.UUID;

/**
 * @author xc
 * @date 2018/11/16
 * @desc
 */
public class DeviceIdUtil {
    /**
     * Get device hardware ID
     *
     * @param context context
     * @return Equipment hardware identification
     */
    public static String getDeviceId(Context context) {
        StringBuilder sbDeviceId = new StringBuilder();

        //Obtain the device default IMEI (> = 6.0 requires ReadPhoneState permission)
        String imei = getIMEI(context);
        //Get AndroidId (no permission required)
        String androidid = getAndroidId(context);
        //Obtain device serial number (no permission required)
        String serial = getSERIAL();
        //Obtain hardware uuid (generate uuid according to hardware related attributes) (no permission is required)
        String uuid = getDeviceUUID().replace("-", "");

        //Append imei
        if (imei != null && imei.length() > 0) {
            sbDeviceId.append(imei);
            sbDeviceId.append("|");
        }
        //Append Android
        if (androidid != null && androidid.length() > 0) {
            sbDeviceId.append(androidid);
            sbDeviceId.append("|");
        }
        //Append serial
        if (serial != null && serial.length() > 0) {
            sbDeviceId.append(serial);
            sbDeviceId.append("|");
        }
        //Add hardware uuid
        if (uuid != null && uuid.length() > 0) {
            sbDeviceId.append(uuid);
        }

        //Generate SHA1 and unify DeviceId length
        if (sbDeviceId.length() > 0) {
            try {
                byte[] hash = getHashByString(sbDeviceId.toString());
                String sha1 = bytesToHex(hash);
                if (sha1 != null && sha1.length() > 0) {
                    //Returns the final DeviceId
                    return sha1;
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        //If none of the above hardware identification data is available,
        //The DeviceId uses the system random number by default, which ensures that the DeviceId is not empty
        return UUID.randomUUID().toString().replace("-", "");
    }

    //Read required_ PHONE_ State permission, > = 6.0, null returned by default
    private static String getIMEI(Context context) {
        try {
            TelephonyManager tm = (TelephonyManager) 
context.getSystemService(Context.TELEPHONY_SERVICE);
            return tm.getDeviceId();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "";
    }

    /**
     * Get the Android ID of the device
     *
     * @param context context
     * @return Android ID of the device
     */
    private static String getAndroidId(Context context) {
        try {
            return Settings.Secure.getString(context.getContentResolver(), 
Settings.Secure.ANDROID_ID);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "";
    }

    /**
     * Obtain the serial number of the equipment (e.g. WTK7N16923005607), which cannot be obtained by individual equipment
     *
     * @return Equipment serial number
     */
    private static String getSERIAL() {
        try {
            return Build.SERIAL;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "";
    }

    /**
     * Get device hardware uuid
     * Use the hardware information to calculate a random number
     *
     * @return Device hardware uuid
     */
    private static String getDeviceUUID() {
        try {
            String dev = "3883756" +
                    Build.BOARD.length() % 10 +
                    Build.BRAND.length() % 10 +
                    Build.DEVICE.length() % 10 +
                    Build.HARDWARE.length() % 10 +
                    Build.ID.length() % 10 +
                    Build.MODEL.length() % 10 +
                    Build.PRODUCT.length() % 10 +
                    Build.SERIAL.length() % 10;
            return new UUID(dev.hashCode(), 
Build.SERIAL.hashCode()).toString();
        } catch (Exception ex) {
            ex.printStackTrace();
            return "";
        }
    }

    /**
     * Take SHA1
     * @param data data
     * @return Corresponding hash value
     */
    private static byte[] getHashByString(String data)
    {
        try{
            MessageDigest  messageDigest = MessageDigest.getInstance("SHA1");
            messageDigest.reset();
            messageDigest.update(data.getBytes("UTF-8"));
            return messageDigest.digest();
        } catch (Exception e){
            return "".getBytes();
        }
    }

    /**
     * To hexadecimal string
     * @param data data
     * @return 16 Hexadecimal Strings 
     */
    private static String bytesToHex(byte[] data){
        StringBuilder sb = new StringBuilder();
        String stmp;
        for (int n = 0; n < data.length; n++){
            stmp = (Integer.toHexString(data[n] & 0xFF));
            if (stmp.length() == 1)
                sb.append("0");
            sb.append(stmp);
        }
        return sb.toString().toUpperCase(Locale.CHINA);
    }
}

call

String deviceId = DeviceIdUtil.getDeviceId(application);

Result output: FE00DDE9298310CDFEEFE69229B8DB248534710F

 

summary

Scheme 1 has great limitations and is not recommended;
Scheme 2 is adopted by many software, because few people delete SD card files; However, attention should be paid to the authority;
Scheme 3 is less restrictive than the first two schemes. As long as the hardware information remains unchanged, the result remains unchanged. Moreover, the scheme can be customized and combined.

What kind of scheme is suitable? We should make a reasonable choice according to our own project needs.

 

Document reproduced:

Unique ID of Android device 

Topics: Java Android Apache