Android中使用OAID

关于

因传统的移动终端设备标识如国际移动设备识别码(IMEI)等已被部分国家认定为用户隐私的一部分,并存在被篡改和冒用的风险,所以在Android 10及后续版本中非厂商系统应用将无法获取IMEI、MAC等设备信息。无法获取IMEI会在用户行为统计过程中对设备识别产生一定影响。近日移动安全联盟针对该问题联合国内手机厂商推出补充设备标准体系方案,选择OAID字段作为IMEI等的替代字段。OAID字段是由中国信通院联合华为、小米、OPPO、VIVO等厂商共同推出的设备识别字段,具有一定的权威性,可满足用户行为统计的使用场景。

移动安全联盟官网: 移动安全联盟刚官网地址
安全联盟SDK、文档以及申请表格: 安全联盟SDK1.0.29文档及SDK

目前支持的机型

厂商名称 支持版本
华为 HMS 2.6.2 及以上
小米 MIUI 10.2 及以上版本
vivo Android 9 及以上版本
OPPO colorOS 6 大部分覆盖,colorOS 7 及以上全覆盖
联想 ZUI 11.4 及以上版本
三星 Android 10 版本
魅族 Android 10 版本
努比亚 Android 10 版本
中兴 Android 10 版本
华硕 Android 10 版本
一加 Android 10 版本
黑鲨 Android 10 版本
摩托罗拉 Android 10 版本
Freeme OS Android 10 版本
酷赛(铂睿智恒) Android 10 版本
Realme colorOS 6 大部分覆盖,colorOS 7 及以上全覆盖
荣耀 Android 10 版本

调用方法

  1. 接入准备——申请证书文件(将需要申请的app信息填写到example_batch.csv表格,然后发送到msa@caict.ac.cn进行申请。注意每个包名对应一个签名,申请时需要将需要申请的全部包名填写到表格中。申请后安全联盟将会把所有申请的证书发送到申请时使用的邮箱)
  • 注意: 关于example_batch.csv表格
    表格中的会员账号:是指在安全联盟登录的账号 可点击进入安全联盟登录页面
    表格中的邮箱:最好填写安全联盟登录账号绑定的邮箱
  1. 把oaid_sdk_x.x.x.aar拷贝到项的libs目录,并设置依赖,其中x.x.x代 表版本号。最新的版本为1.0.29

  2. 将证书文件(应用包名.cert.pem)、 supplierconfig.json 文件拷贝到项目 assets 目录下,(只获取oaid信息则不需要修改json配置文件,只需原样放到assets目录下即可。如果想要使用VAID,可修改里边对应内容,特别是需要设置 appid 的部分,要去对应厂商的应用商店里注册自己的 app,来获取对应appid。)

  3. 设置依赖

    implementation files(‘libs/oaid_sdk_1.0.29.aar’)
  4. 混淆设置

    # sdk
    -keep class com.bun.miitmdid.** { *; }
    # asus
    -keep class com.asus.msa.SupplementaryDID.** { *; }
    -keep class com.asus.msa.sdid.** { *; }
    # freeme
    -keep class com.android.creator.** { *; }
    -keep class com.android.msasdk.** { *; }
    # huawei
    -keep class com.huawei.hms.ads.identifier.** { *; }
    #-keep class com.uodis.opendevice.aidl.** { *; }
    # lenovo
    -keep class com.zui.deviceidservice.** { *; }
    -keep class com.zui.opendeviceidlibrary.** { *; }
    # meizu
    -keep class com.meizu.flyme.openidsdk.** { *; }
    # nubia
    -keep class com.bun.miitmdid.provider.nubia.NubiaIdentityImpl
    # oppo
    -keep class com.heytap.openid.** { *; }
    # samsung
    -keep class com.samsung.android.deviceidservice.** { *; }
    # vivo
    -keep class com.vivo.identifier.** { *; }
    # xiaomi
    -keep class com.bun.miitmdid.provider.xiaomi.IdentifierManager
    # zte
    -keep class com.bun.lib.** { *; }
    # coolpad
    -keep class com.coolpad.deviceidsupport.** { *; }
  1. 设置 gradle 编译选项,开发者可以根据自己对平台的选择进行合理配置

    ndk { abiFilters  'armeabi-v7a','x86','arm64-v8a','x86_64','armeabi' }

    注意:考虑到 sdk 兼容性,sdk 包默认集成了常用 abi 的 so,包括 armeabi-v7a,arm64-v8a, x84, x84_64 共四种。如果需要减小 SDK 体积,可以使用压缩工具打开 aar 文件,手动删除多余的架构。

  2. 添加 oaid管理文件MiitHelper.java

根据官网源文件DemoHelper.java修改而来,可以根据自己需求修改,这里是cocos工程,这里将文件MiitHelper.java放到org.cocos2dx.javascript目录中。

package org.cocos2dx.javascript;

import android.content.Context;
import android.util.Log;

import com.bun.miitmdid.core.InfoCode;
import com.bun.miitmdid.core.MdidSdkHelper;
import com.bun.miitmdid.interfaces.IIdentifierListener;
import com.bun.miitmdid.interfaces.IdSupplier;
import com.bun.miitmdid.pojo.IdSupplierImpl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class MiitHelper implements IIdentifierListener {
    public static final String TAG = "MiitHelper";
    public static final int HELPER_VERSION_CODE = 20210928; // DemoHelper版本号
    private AppIdsUpdater appIdsUpdater;
    private boolean isCertInit = false;

    public final boolean isSDKLogOn = true;                           // 1)设置 是否开启sdk日志
    public static String ASSET_FILE_NAME_CERT = "";                // 2)设置 asset证书文件名

    public static void Init(Context context, AppIdsUpdater listener){
        MiitHelper miitHelper = new MiitHelper();

        ASSET_FILE_NAME_CERT = context.getPackageName()+".cert.pem";
        Log.e(TAG,ASSET_FILE_NAME_CERT);

        System.loadLibrary("nllvm1632808251147706677");  // 加固版本在调用前必须载入SDK安全库
        if(MdidSdkHelper.SDK_VERSION_CODE != HELPER_VERSION_CODE){
            Log.w(TAG,"SDK version not match.");
            throw new RuntimeException("SDK version not match.");
        }
        miitHelper.appIdsUpdater = listener;

        // 获取设备号
        miitHelper.getDeviceIds(context);
    }

    /**
     * 获取OAID
     * @param cxt
     */
    public void getDeviceIds(Context cxt){
        // TODO (4)初始化SDK证书
        if(!isCertInit){ // 证书只需初始化一次
            // 证书为PEM文件中的所有文本内容(包括首尾行、换行符)
            isCertInit = MdidSdkHelper.InitCert(cxt, loadPemFromAssetFile(cxt, ASSET_FILE_NAME_CERT));
            if(!isCertInit){
                Log.w(TAG, "getDeviceIds: cert init failed");
            }
        }

        //(可选)设置InitSDK接口回调超时时间(仅适用于接口为异步),默认值为5000ms.
        // 注:请在调用前设置一次后就不再更改,否则可能导致回调丢失、重复等问题
        MdidSdkHelper.setGlobalTimeout(5000);

        // TODO (5)调用SDK获取ID
        int code = MdidSdkHelper.InitSdk(cxt, isSDKLogOn, this);

        // TODO (6)根据SDK返回的code进行不同处理
        IdSupplierImpl unsupportedIdSupplier = new IdSupplierImpl();
        if(code == InfoCode.INIT_ERROR_CERT_ERROR){                         // 证书未初始化或证书无效,SDK内部不会回调onSupport
            // APP自定义逻辑
            Log.w(TAG,"cert not init or check not pass");
            onSupport(unsupportedIdSupplier);
        }else if(code == InfoCode.INIT_ERROR_DEVICE_NOSUPPORT){             // 不支持的设备, SDK内部不会回调onSupport
            // APP自定义逻辑
            Log.w(TAG,"device not supported");
            onSupport(unsupportedIdSupplier);
        }else if( code == InfoCode.INIT_ERROR_LOAD_CONFIGFILE){            // 加载配置文件出错, SDK内部不会回调onSupport
            // APP自定义逻辑
            Log.w(TAG,"failed to load config file");
            onSupport(unsupportedIdSupplier);
        }else if(code == InfoCode.INIT_ERROR_MANUFACTURER_NOSUPPORT){      // 不支持的设备厂商, SDK内部不会回调onSupport
            // APP自定义逻辑
            Log.w(TAG,"manufacturer not supported");
            onSupport(unsupportedIdSupplier);
        }else if(code == InfoCode.INIT_ERROR_SDK_CALL_ERROR){             // sdk调用出错, SSDK内部不会回调onSupport
            // APP自定义逻辑
            Log.w(TAG,"sdk call error");
            onSupport(unsupportedIdSupplier);
        } else if(code == InfoCode.INIT_INFO_RESULT_DELAY) {             // 获取接口是异步的,SDK内部会回调onSupport
            Log.i(TAG, "result delay (async)");
        }else if(code == InfoCode.INIT_INFO_RESULT_OK){                  // 获取接口是同步的,SDK内部会回调onSupport
            Log.i(TAG, "result ok (sync)");
        }else {
            // sdk版本高于DemoHelper代码版本可能出现的情况,无法确定是否调用onSupport
            // 不影响成功的OAID获取
            Log.w(TAG,"getDeviceIds: unknown code: " + code);
        }
    }

    /**
     * APP自定义的getDeviceIds(Context cxt)的接口回调
     * @param supplier
     */
    @Override
    public void onSupport(IdSupplier supplier) {
        if(supplier==null) {
            Log.w(TAG, "onSupport: supplier is null");
            return;
        }
        if(appIdsUpdater ==null) {
            Log.w(TAG, "onSupport: callbackListener is null");
            return;
        }
        // 获取Id信息
        // 注:IdSupplier中的内容为本次调用MdidSdkHelper.InitSdk()的结果,不会实时更新。 如需更新,需调用MdidSdkHelper.InitSdk()
        boolean isSupported = supplier.isSupported();
        boolean isLimited  = supplier.isLimited();
        String oaid=supplier.getOAID();
        String vaid=supplier.getVAID();
        String aaid=supplier.getAAID();

        //TODO (7) 自定义后续流程,以下显示到UI的示例
        String idsText= "support: " + (isSupported ? "true" : "false") +
                "\nlimit: " + (isLimited ? "true" : "false") +
                "\nOAID: " + oaid +
                "\nVAID: " + vaid +
                "\nAAID: " + aaid + "\n";
        Log.d(TAG, "onSupport: ids: \n" + idsText);
        appIdsUpdater.onIdsValid(oaid);
    }

    public interface AppIdsUpdater {
        void onIdsValid(String oaid);
    }

    /**
     * 从asset文件读取证书内容
     * @param context
     * @param assetFileName
     * @return 证书字符串
     */
    public static String loadPemFromAssetFile(Context context, String assetFileName){
        try {
            InputStream is = context.getAssets().open(assetFileName);
            BufferedReader in = new BufferedReader(new InputStreamReader(is));
            StringBuilder builder = new StringBuilder();
            String line;
            while ((line = in.readLine()) != null){
                builder.append(line);
                builder.append('\n');
            }
            return builder.toString();
        } catch (IOException e) {
            Log.e(TAG, "loadPemFromAssetFile failed");
            return "";
        }
    }
}
  1. 调用

    ActivityonCreate函数中添加初始化。这是异步调用,使用需要注意一下。

public class AppActivity extends Cocos2dxActivity implements ActivityCompat.OnRequestPermissionsResultCallback {
    public static String oaid = "";

    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...

        MiitHelper.Init(this, new MiitHelper.AppIdsUpdater(){
            @Override
            public void onIdsValid(String oaid){
                AppActivity.oaid = oaid;
            }
        });
    }

    ....
}
0%