Your support means a lot to me!

🔥 Hi, I’m Xu Rui. This article has been included in GitHub · Android-Notebook. Here are Android advanced growth route notes & blog, like-minded friends, welcome to grow with me. (Contact & Group entry on GitHub)

1. Introduction

  • Device identifiers such as IMEI have been identified as part of user privacy. Obtaining or even frequently obtaining IMEI in unnecessary scenarios will be regarded as illegal acquisition of user information.
  • Starting from Android 10, applications cannot obtain unique identifiers such as IMEI and MACREAD_PRIVILEGED_PHONE_STATEPermission can also be obtained later, but this permission can only be obtained by the system application. If forced fetching is applied, either a NULL message is obtained or a SecurityException occurs (depending ontargetSdkVersion);
  • In order to solve this problem, the relevant ministries and commissions established the Mobile Security Alliance (MSA) and formulated a set of “Mobile Intelligent Terminal Supplementary Equipment Identification System”. Because one of the most important identifiers is the OAID Anonymous device Identifier, some people call this system simply OAID.

2. Supplement the equipment identification system

The supplementary equipment identification system is mainly divided into four layers:

  • UUID device unique identifiers are not dependent on this system. They are hardwired to the hardware when the device is delivered and will not be reset even if factory Settings are restored.
  • OAID An anonymous device ID is a substitute for the UUID. It is generated when a terminal is started for the first time. The OAID of the same device is the same, so it can be shared among multiple applications. Restoring the factory Settings will reset the OAID.
  • The VAID Developer Anonymous Device identifier is the developer dimension ID that is generated when the application is installed. All applications from the same developer on the same device have the same VAID. In other cases, the VAID is different. Reinstalling the application will reset the VAID.
  • AAID The application anonymous device identifier is the ID of the application sandbox dimension, which is generated during application installation. Even if the app is on the same device and by the same developer, the AAID is different. Reinstalling or cleaning user data will reset the AAID.
English abbreviations English full name Chinese name describe Reset the sex Application scenarios
UDID Unique Device Identifier Unique identifier of the device Unique hardware identifier of a device, such as the IMEI, is generated by the hardware information before delivery It cannot be reset after leaving the factory Equipment unique identification, such as advertising business in the advertising effect attribution
OAID Open Anonymous Device Identity Anonymous device identifier OAID is a substitute for UUID, which is the device dimension ID. Generated when the terminal is started for the first time, the OAID of the same device is the same OAID will reset after factory Settings are restored Equipment unique identification, such as advertising business in the advertising effect attribution
VAID Vender Anonymous Device Identity Developer anonymous device identifier VAID is the ID of the developer dimension. Generated when the application is installed, all applications on the same device and by the same developer have the same VAID, otherwise the VAID is different VAID will be reset after factory Settings are restored and the application is reinstalled. (Exception: When uninstalling an application, the device will not be reset if another application of the same developer has been read by VAID.) Developer unique identifier if unified developer different yingying between recommendations
AAID Application Anonymous Device Identity Apply anonymous device identifiers AAID is the ID of the applied sand table dimension. The AAID is generated when the application is installed, even if it is on the same device and by the same developer Exception: When uninstalling an application, the AAID will not be reset if another application of the same developer has been read by the AAID.) Unique identifier of an application that can be used for user statistics

3. Preparation

  • To register an MSA account:

According to the requirements of MSA, you need to register an enterprise account before downloading SDK and integration documents. In this step, you can submit relevant information and materials according to the guidelines. Generally, it can be approved in 1 to 2 working days.

  • Application for SDK Certificate:

Starting from V1.0.26, the SDK introduces the certificate verification mechanism. Each APP needs to apply for a certificate file (package name.cert.pem), and only the APP whose package name matches the certificate can normally obtain the supplementary device ID. The validity period of the certificate is 1 year by default. If the certificate expires, obtaining the supplementary device ID will be affected. Therefore, you need to design a certificate update mechanism based on the actual situation. For example, you need to build a default certificate in the application and update the certificate from the background server in advance when the application starts and expires. To apply for a certificate, you need to send an application email to [email protected] with the form example_Batch.csv, for example:

  • Download the SDK and integration documentation:

After registering and approving the enterprise account, you can download the relevant materials from the official website (because MSA prohibits third parties from illegally distributing the SDK, so you still have to download by yourself).

  • Vivo Store AppID:

    Because the scheme of Vivo mobile phone is different, you need to use the APPID applied for by your app in the app store when configuring the SDK.


4. Integration and encapsulation

  • Integration of aar:Rely onoaid_sdk_x.x.x.aar, specific method you can certainly;
  • APPID configuration: å°† supplierconfig.jsonCopy to project /main/assets directory and modify the APPID configuration inside, mainly modify vivo configuration, for example:

  • Certificate configuration: å°† The package name. Cert. PemCopy the certificate file to the project /main/assets directory, for example:

  • Configuration obfuscation: Configure the obfuscation configuration listed in the SDK integration documentation.

  • Gradle compilation options: Configure the NDK compilation configuration for your project, for example:

    // ndk {
    // abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a', 'x86_64'
    // }
    splits {
        abi {
            enable true
            reset()
            include 'armeabi-v7a'.'arm64-v8a'
            universalApk true
        }
    }
    packagingOptions {
        doNotStrip "*/armeabi-v7a/*.so"
        doNotStrip "*/arm64-v8a/*.so"
    }
    Copy the code
  • Code encapsulation: After completing the above integration and configuration steps, all that remains is to call the SDK interface to get the OAID. In the official Demo, the code quality of DemoHelper is not very good. I have compiled a simple version, and the code is not difficult. You can see it directly. There are a few points to note:

    • 1. Privacy Policy authorization: It is necessary to ensure that users agree with the Privacy Policy before initializing the SDK.
    • 2. Initialization time:The hardened version requires firstMsaoaidsec loadLibrary (" "), due to the loading delay, so the official recommendation as early as early initialization;
    • 3. Callback timing: MidSdkHelper#InitSdk()The result callback may be synchronous or callback from a different step thread, depending on the SDK’s internal judgment.

IOAIDApi.kt

interface IOAIDApi {

    /** * 1. Initialize OAID SDK * 2. The hardened version must load the SDK security library before calling it, because of the loading delay. It is recommended to call the loadLibrary method * * in Application@paramDebug Specifies whether to debug. The debug state enables SDK log output */
    fun init(debug: Boolean)

    /** * gets the ID. The callback may be synchronous or asynchronous */
    @AnyThread
    fun fetchDeviceIds(callback: (OAIDResult) - >Unit)
}
Copy the code

OAID.kt

internal class OAID private constructor(
    context: Context
) : IOAIDApi {

    // ApplicationContext
    private val context: Context = context.applicationContext

    /** * Certificate initialization flag, true: already initialized */
    @Volatile
    private var isCertInit: Boolean = false

    /** * Whether the certificate is valid, true: valid */
    private var isCertValid: Boolean = false

    /** * Certificate expiration time, NULL: certificate invalid */
    private var certExpDate: Date? = null

    /** * Debugging */
    private var debug: Boolean = false

    companion object {

        @Volatile
        private var _oaid: IOAIDApi? = null

        @AnyThread
        fun oaid(context: Context): IOAIDApi {
            if (null == _oaid) {
                synchronized(IOAIDApi::class.java) {
                    if (null == _oaid) {
                        _oaid = OAIDImpl(context)
                    }
                }
            }
            return_oaid!! }}override fun init(debug: Boolean) {
        System.loadLibrary("msaoaidsec")

        this.debug = debug
    }

    override fun fetchDeviceIds(callback: (OAIDResult) - >Unit) {
        // 1. Verify and initialize certificates
        checkCertValidity()

        // 2.1 ID provider that provides null information
        val unsupportedIdSupplier = IdSupplierImpl()

        // 2.2 Unified callback receiver
        vallistener = IIdentifierListener { supplier: IdSupplier? -> supplier? .let {/ / callback
                callback(
                    OAIDResult(
                        supplier.isSupported, supplier.isLimited, supplier.oaid, supplier.vaid, supplier.aaid
                    )
                )
            }
        }

        if(! isCertValid) {// The certificate is invalid
            listener.onSupport(unsupportedIdSupplier)
            return
        }

        // 3. Invoke SDK interface to obtain OAID
        val code = try {
            MdidSdkHelper.InitSdk(context, debug, listener)
        } catch (error: Error) {
            error.printStackTrace()
            -1
        }
        // 4. Handle exceptions
        when (code) {
            InfoCode.INIT_ERROR_CERT_ERROR,             // If the certificate is not initialized or invalid, the SDK does not call onSupport internally
            InfoCode.INIT_ERROR_DEVICE_NOSUPPORT,       // The SDK does not call back onSupport for unsupported devices
            InfoCode.INIT_ERROR_LOAD_CONFIGFILE,        // Failed to load the configuration file. The SDK does not call onSupport internally
            InfoCode.INIT_ERROR_MANUFACTURER_NOSUPPORT, // The SDK does not call back onSupport for unsupported device vendors
            InfoCode.INIT_ERROR_SDK_CALL_ERROR          // SDK call error, SDK internal does not call onSupport- > {// If an exception occurs, an empty message is directly called
                listener.onSupport(unsupportedIdSupplier)
            }
            InfoCode.INIT_INFO_RESULT_DELAY,            // The fetch interface is asynchronous, and the SDK calls onSupport internally
            InfoCode.INIT_INFO_RESULT_OK                // The fetch interface is synchronous, and the SDK calls onSupport internally- > {// do nothing
            }
            else- > {// do nothing}}}/** * Verify and initialize certificates *@returnTrue: The certificate is initialized successfully. False: Certificate initialization failed */
    private fun checkCertValidity(a) {

        /** * Read the certificate contents from the asset file **@returnCertificate character string */
        fun loadPemFromAssetFile(a): String {
            return try {
                // File name of the certificate
                val certFileName = context.packageName + ".cert.pem"
                val inputStream = context.assets.open(certFileName)
                val bufferReader = BufferedReader(InputStreamReader(inputStream))
                val builder = StringBuilder()
                var line: String?
                while(bufferReader.readLine().also { line = it } ! =null) {
                    builder.append(line)
                    builder.append('\n')
                }
                builder.toString()
            } catch (e: IOException) {
                ""}}/** * Resolve the certificate expiration time *@returnExpiration time, null */ is returned if the certificate is invalid
        fun getCertExpDate(certStr: String): Date? {
            return try {
                // Certificate entity
                val cert = CertificateFactory.getInstance("X.509").generateCertificate(certStr.byteInputStream()) as X509Certificate
                // Verify the validity of the certificate and throw an exception if the certificate expires
                cert.checkValidity()
                cert.notAfter
            } catch (ex: Exception) {
                // The certificate is invalid
                null}}// DCL
        if (isCertInit) {
            // Initialization only needs to be done once, returning the last result
            return
        }
        synchronized(IOAID::class) {
            if (isCertInit) {
                return
            }
            isCertInit = true
            // File name of the certificate
            val certStr = loadPemFromAssetFile()
            // Certificate expiration time
            certExpDate = getCertExpDate(certStr)
            // TODO If your application scenario requires very high certificate validity, you can download and update the certificate in advance at this time
            // Initialize the certificate. The certificate needs to be initialized only once
            isCertValid = MdidSdkHelper.InitCert(context, certStr)
        }
    }
}
Copy the code

5. Other details

  • Privacy Policy (Important) : Because OAID belongs to a third party SDK, you need to establish a privacy policy regarding the use of the SDK to obtain ids, such as a list of third party SDKS:
The name of the SDK Scenario description and special description Refers to the main types of personal information collected Name of the data receiver Privacy Policy Link
OAID Get OAID and make effect attribution at the time of advertising Device information (Unique device Identifier) China Academy of Information and Communication Technology www.msa-alliance.cn/
  • Interfaces provided by the SDK:

    • IdSupplier#isSupported() : determines whether the device supports supplementary device identifiers
    • IdSupplier#isLimited() : determines whether the device restricts the application from obtaining supplementary device identifiers
    • IdSupplier#getOAID(Context) : getOAID
    • IdSupplier#getVAID(Context) : getVAID
    • IdSupplier#getAAID(Context) : getAAID
  • Does the SDK need to be connected?

Most manufacturers require networking when invoking interfaces, for example, VAID and AAID need to be verified and calculated in the background of the manufacturer

  • How does the SDK internally determine whether the app is from the same developer?

VAID is defined as the ID of the device + developer dimension. All applications from the same developer on the same device have the same VAID. Otherwise, VAID varies. Different phone manufacturers use different ways to determine whether they are the same developer. Some use AppId directly, such as Vivo. Some are determined by application signature information, such as OPpo. Scheme based on AppID, ask us to fill in in the configuration file in application distribution in the mall AppID (and configure to assets/supplierconfig. The json), based on the application of the signature scheme, the better for our members.

  • Does the SDK support emulators?

Supplementary device identifiers are essentially vendor-provided capabilities, so only real machines support them, not emulators. See the SDK documentation for a list of supported real machines:

— Screenshot from MSA official document

  • How to determine the validity period of the certificate?

Can parse through code, also can make a copy of the certificate content to www.pianyissl.com/tools/cer_d. Online parsing, for example:

If you are not familiar with digital certificates, you can review our previous discussion: Encryption, digest, Signature, certificate, Once again!

The resources

  • MSA’s official website
  • OAID integration tutorial

Recently, I set up an Android exchange group, hope we can discuss technology together, find like-minded friends, need can scan code into the group!