Android solves device ID acquisition exception java.lang.SecurityException: getDeviceId: The user 10612 does not meet the require

Problem recovery

Today I built a new project using the development version with compileSdkVersion of 29, and also adjusted the targetSdkVersion to 29. A crash exception occurred when calling the device ID. Check the log as follows:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.smart.artifact.sdk/com.smart.artifact.sdk.MainActivity}: java.lang.SecurityException: getDeviceId: The user 10612 does not meet the requirements to access device identifiers.
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3308)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3457)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2044)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7562)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
     Caused by: java.lang.SecurityException: getDeviceId: The user 10612 does not meet the requirements to access device identifiers.
        at android.os.Parcel.createException(Parcel.java:2074)
        at android.os.Parcel.readException(Parcel.java:2042)
        at android.os.Parcel.readException(Parcel.java:1990)
        at com.android.internal.telephony.ITelephony$Stub$Proxy.getDeviceId(ITelephony.java:10389)
        at android.telephony.TelephonyManager.getDeviceId(TelephonyManager.java:1631)

The location error was found when an abnormal crash occurred when obtaining the device ID. My device ID acquisition code is as follows:

TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            if (tm!=null) {
                if (TextUtils.isEmpty(tm.getDeviceId())==false)
                    deviceId= tm.getDeviceId();
                 else
                    deviceId= Settings.Secure.getString(context.getApplicationContext().getContentResolver(),
                            Settings.Secure.ANDROID_ID);
            }
            else
                deviceId= Settings.Secure.getString(context.getApplicationContext().getContentResolver(),
                        Settings.Secure.ANDROID_ID);

Here, I use different versions of mobile phones to obtain the device ID number. The results are different. If I use this code to obtain the device ID on a low-version device smaller than Android10, the above exception will not occur. However, if I use this code to obtain the device ID on Android10 and above versions, The device ID will cause a crash exception. Baidu searched for relevant information and found that in Android versions after 10, obtaining the device ID through TelephonyManager requires the application to have the READ_PRIVILEGED_PHONE_STATE privilege to access the device’s non-resettable identifier, including IMEI and serial number. Otherwise, a system security exception will occur and crash.
Here is an explanation of the READ_PRIVILEGED_PHONE_STATE permission. This is called the read privileged phone permission. The adding method is the same as most permissions. However, it should be noted that this permission must be added by a system application, so most third-party applications cannot successfully add this permission. .

<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />

Affected by this, in Android10 and later versions, it is impossible to obtain the device ID or IMEI number through the above code. Here we summarize the specific APIs that cannot be called.
, refer to Google official documentation as follows:

Build
getSerial()

TelephonyManager
getImei()
getDeviceId()
getMeid()
getSimSerialNumber()
getSubscriberId()

Solve the problem
I took a look at the solutions online. Some people said that it is enough to lower the targetSdkVersion to 28. In fact, it will not crash after lowering it to 28, but it also brings some problems. First of all, if you use a lot of them in your project After API 29, you need to manually adjust these code modules when you reduce it to 28. This is not difficult to do in a while. The key to the problem is that the value obtained by tm.getDeviceId() is still empty in devices with Android 10 and above. , the reason has been mentioned before, this is a limitation of Google, the reduction to 28 just means that the API with SDK version 28 called on our Android10 device can avoid crashes. Here we will explain what the three versions of minSdkVersion, compileSdkVersion and targetSdkVersion represent?

1.minSdkVersion
This is the version number of the mobile phone system that represents the minimum supported version of the developed application. If the version number of the mobile phone system is lower than minSdkVersion, it will not be usable.

2. compileSdkVersion
This indicates the SDK version number currently used for development and compilation. For example, compileSdkVersion 29 means that we can use the API and corresponding method attributes in SDK 29.

3. targetSdkVersion
This indicates the system target compatible version number. How do you understand it? We understand it from two aspects. First, a high-version mobile phone runs a low-version SDK. For example, if your mobile phone is Android 10 (SDK 29), and the targetSdkVersion is set to 20, the API features of SDK 20 are still called on the device. , here explains why the crash does not occur when my targetSdkVersion drops to 28 on an Android 10 phone, because the API of SDK 28 is called, even if it is the same method name, the version processing is different. . Secondly, when running a higher version of the SDK on a lower version, for example, setting the targetSdkVersion to 29 on Android8 (SDK 27), it is time to deal with related compatibility issues, such as f(Build.VERSION.SDK_INT<29) {…}else{…}.

So in the end I posted my solution as follows:

public static String getDeviceId(Context context) {
        String deviceId="";
        try {
        if(Build.VERSION.SDK_INT<29) {
            TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            if (tm!=null) {
                if (TextUtils.isEmpty(tm.getDeviceId())==false)
                    deviceId= tm.getDeviceId();
                 else
                    deviceId= Settings.Secure.getString(context.getApplicationContext().getContentResolver(),
                            Settings.Secure.ANDROID_ID);
            }
            else
                deviceId= Settings.Secure.getString(context.getApplicationContext().getContentResolver(),
                        Settings.Secure.ANDROID_ID);
        }
        else
            deviceId=Settings.Secure.getString(context.getApplicationContext().getContentResolver(),
                    Settings.Secure.ANDROID_ID);
        }catch (Exception e)
        {e.printStackTrace();}
       return deviceId;
    }

For versions after Android 10, I will use Settings.Secure.getString(context.getApplicationContext().getContentResolver(),
Settings.Secure.ANDROID_ID) instead.

The knowledge points of the article match the official knowledge files, and you can further learn related knowledge. Java Skill TreeHomepageOverview 138192 people are learning the system