Android basis: how to determine the unique identifier Android device

This is a reposted article, the original link below. If infringement, delete the contact.
link

Here Insert Picture Description
Unique identification must meet two characteristics unique to the perfect positioning equipment to solve the problem, but the solution to this problem is destined only limit close to perfection:
(1) Uniqueness: ID must be unique across all devices use the application
(2 ) invariance: identity must be maintained on the same device

A direction of a: Use hardware identification

In fact hardware identification hardware of production will be required to meet these two characteristics (still uncertainty artificially produced), but identified by the acquired resistance tends to be difficult, such as the use of the hardware identification code uniquely identifying the program that can be used the more narrow range, can not be used as a global program.

1. DEVICE_ID

TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
String deviceId = tm.getDeviceId();

2. Use ANDROID_ID

String androidId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);

3. MAC ADDRESS

By acquiring Mac address as Bluetooth or wifi unique identification number

wifiManager = ((WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE));
String macAddress = wifiManager.getConnectionInfo().getMacAddress();

4. Use SERIAL NUMBER

To get through android.os.Build.SERIAL

5. Advantages and limitations of hardware identification

Advantages: almost perfect to meet the uniqueness and invariance

Disadvantages:
Here Insert Picture Description

II. UUID using two directions

This is the official recommended way to generate a unique identification code generation, a little different, the official scheme (where) there is an application UUID generated among internal storage, APP unloading heavy equipment cause changes to occur; we actually use them It can be stored to external storage, unless artificially deleted, corrupted, so that it is also guaranteed invariance, and its uniqueness guaranteed by the UUID.

The principle of the UUID Analysis:
Wiki Explanation: Universally Unique Identifier (English: Universally Unique Identifier, abbreviation: UUID) is a computer system identification information to a number of 128-bit identifier, as well as related terms: globally unique identifier (GUID). Generated according to standard methods, and does not rely on dispensing a central registration mechanism, the UUID is unique, unlike most other numbering schemes. Repeat UUID code probability close to zero, negligible

组成: 8-4-4-4-12 xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx M表示 UUID 版本,数字 N的一至三个最高有效位表示 UUID 变体

UUID根据版本不同,依赖的组成有不同的变种,基于时间的UUID版本是通过计算当前时间戳、随机数和机器MAC地址得到 。UUID的核心算法保证了即使在多处理器同时生成的UUID重复性为0,因为他们所在的时间、空间(节点:通常是MAC地址)必然不一致。

由于在算法中使用了MAC地址,这个版本的UUID可以保证在全球范围的唯一性。但与此同时,使用MAC地址会带来安全性问题,这就是这个版本UUID受到批评的地方。如果应用只是在局域网中使用,也可以使用退化的算法,以IP地址来代替MAC地址--Java的UUID往往是这样实现的(当然也考虑了获取MAC的难度)。

String uniqueID = UUID.randomUUID().toString();

三. 趋于完美的方案

尽可能的获取硬件标识来满足两个特性,在有限制或其他因素的条件下,尽可能满足不变性,将UUID存储在外部环境来进行读写。

(1)方案思路
尽可能的获取硬件标识
硬件标识为空,进行UUID的生成、存储

(2)方案说明:
需要在使用之前拿到设备信息权限(没有会导致DeviceID不可取,但仍然可用),外部存储读写权限(必须,否则不可用)
最好在Application中使用,唯一标识在app与服务器直接交互很常用,放在全局统一的地方方便管理使用

还有一种方案是拿到设备的某些唯一信息,生成特定的UUID,这样保持不变就可以跳过存储,但是既然拿到了唯一信息,那为啥还要生成UUID呢?

public class UniqueIDUtils {

    private static final String TAG = "UniqueIDUtils";
    private static String uniqueID;
    private static String uniqueKey = "unique_id";
    private static String uniqueIDDirPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath();
    private static String uniqueIDFile = "unique.txt";


    public static String getUniqueID(Context context) {
        //三步读取:内存中,存储的SP表中,外部存储文件中
        if (!TextUtils.isEmpty(uniqueID)) {
            Log.e(TAG, "getUniqueID: 内存中获取" + uniqueID);
            return uniqueID;
        }
        uniqueID = PreferenceManager.getDefaultSharedPreferences(context).getString(uniqueKey, "");
        if (!TextUtils.isEmpty(uniqueID)) {
            Log.e(TAG, "getUniqueID: SP中获取" + uniqueID);
            return uniqueID;
        }
        readUniqueFile(context);
        if (!TextUtils.isEmpty(uniqueID)) {
            Log.e(TAG, "getUniqueID: 外部存储中获取" + uniqueID);
            return uniqueID;
        }
        //两步创建:硬件获取;自行生成与存储
        getDeviceID(context);
        getAndroidID(context);
        getSNID();
        createUniqueID(context);
        PreferenceManager.getDefaultSharedPreferences(context).edit().putString(uniqueKey, uniqueID);
        return uniqueID;
    }

    @SuppressLint("MissingPermission")
    private static void getDeviceID(Context context) {
        if (!TextUtils.isEmpty(uniqueID)) {
            return;
        }
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O_MR1) {
            return;
        }
        String deviceId = null;
        try {
            deviceId = ((TelephonyManager) context.getSystemService(TELEPHONY_SERVICE)).getDeviceId();
            if (TextUtils.isEmpty(deviceId)) {
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        uniqueID = deviceId;
        Log.e(TAG, "getUniqueID: DeviceId获取成功" + uniqueID);
    }

    private static void getAndroidID(Context context) {
        if (!TextUtils.isEmpty(uniqueID)) {
            return;
        }
        String androidID = null;
        try {
            androidID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
            if (TextUtils.isEmpty(androidID) || "9774d56d682e549c".equals(androidID)) {
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        uniqueID = androidID;
        Log.e(TAG, "getUniqueID: AndroidID获取成功" + uniqueID);
    }

    private static void getSNID() {
        if (!TextUtils.isEmpty(uniqueID)) {
            return;
        }
        String snID = Build.SERIAL;
        if (TextUtils.isEmpty(snID)) {
            return;
        }
        uniqueID = snID;
        Log.e(TAG, "getUniqueID: SNID获取成功" + uniqueID);
    }


    private static void createUniqueID(Context context) {
        if (!TextUtils.isEmpty(uniqueID)) {
            return;
        }
        uniqueID = UUID.randomUUID().toString();
        Log.e(TAG, "getUniqueID: UUID生成成功" + uniqueID);
        File filesDir = new File(uniqueIDDirPath + File.separator + context.getApplicationContext().getPackageName());
        if (!filesDir.exists()) {
            filesDir.mkdir();
        }
        File file = new File(filesDir, uniqueIDFile);
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(file);
            outputStream.write(uniqueID.getBytes());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void readUniqueFile(Context context) {
        File filesDir = new File(uniqueIDDirPath + File.separator + context.getApplicationContext().getPackageName());
        File file = new File(filesDir, uniqueIDFile);
        if (file.exists()) {
            FileInputStream inputStream = null;
            try {
                inputStream = new FileInputStream(file);
                byte[] bytes = new byte[(int) file.length()];
                inputStream.read(bytes);
                uniqueID = new String(bytes);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static void clearUniqueFile(Context context) {
        File filesDir = new File(uniqueIDDirPath + File.separator + context.getApplicationContext().getPackageName());
        deleteFile(filesDir);
    }

    private static void deleteFile(File file) {
        if (file.isDirectory()) {
            for (File listFile : file.listFiles()) {
                deleteFile(listFile);
            }
        } else {
            file.delete();
        }
    }
}

四.希望但又矛盾的完美方案

硬件标识既然对获取方关闭,那提供基于硬件标识生成的标识(类似UUID)暴露给获取方,但Android10上对于设备隐私的控制又明确了Google是不想app能够长久定位同一台设备的。不过如果基于硬件标识及app包名来生成的呢?

名词解释
Here Insert Picture Description

Published 81 original articles · won praise 37 · views 50000 +

Guess you like

Origin blog.csdn.net/gaolh89/article/details/103987866