Android 安全整改检测框架

背景

一、因工信部整改要求,现编写检测工具如下功能:

  • 1、禁止在用户未点击同意前,获取用户手机设备信息。
  • 2、禁止在用户未点击同意前,发起网络接口请求
  • 3、禁止在用户点击同意前,初始化第三方SDK,防止上报隐私信息;
  • 4、禁止调用获取软件安装列表等敏感android api接口;

二、集成步骤

1、在根项目的build.gradle中添加以下内容:

arduino 复制代码
dependencies {
    classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.8'
}

2、在需要使用module的build.gradle中添加(app则不用)

bash 复制代码
plugins {
    id 'android-aspectjx'
}

3、添加依赖

arduino 复制代码
dependencies {
    implementation "com.meiyou.aspectj:aspectj:1.2.0" //具体请参照update.md使用最新版本
}

4、注意(重要重要重要!!!!!!!!!!!!!!!)

必须实现IPrivacyPolicyStub中的方法:

kotlin 复制代码
@Protocol("PrivacyPolicyStub")
public class PrivacyPolicyStub {

    /**
     * 是否同意过首次进入app的隐私协议
     */
    public boolean isAcceptFirstStartApp() {
        return MainActivity.isAccepted;
    }
}

依赖包中会实时去获取isAcceptFirstStartApp()的值,当用户同意首次进入app的隐私协议后,需要及时改变返回的值。

5、确认主工程app/build.gradle有应用插件:

arduino 复制代码
apply plugin: 'android-aspectjx'
aspectjx {
 exclude 'com.alipay', 'com/alipay'
}
scss 复制代码
支付宝SDK必须exclude(不exclude会导致支付出现ClassNotFound),采用此种全局扫描,避免遗漏

6、如果原本自己的app有集成AspectjFix用于整改,对比一下这个库里边的AspectjUtil类,有重复的话,app里需要进行删除,避免重复hook造成的问题,因为这个库全局公用(重要重要重要!!!!!!!!!!!!!!!!!!!!)

三、测试方法

1、面向安全人员和产品的测试方法:

  • 1、下载fbflipper.com 工具,并打开,等待手机连接;
  • 2、安装美柚Test版本或者Debug版本(这两个版本有日志),手机USB连接电脑,连接成功后,Flippe软件会输出app的logcat日志
  • 3、在Flipper软件上过滤 "SAFE"字样
  • 4、操作APP,查看日志是否有异常即可,比如:

2、面向测试和开发人员的测试方法:

  • 可参考测试方法1或者按如下步骤

  • 1、编译Test或者Debug包(有日志就行)手机挂上Charles代理;

  • 2、操作以下过程:卸载重装app,打开app,停留隐私弹窗界面,点击隐私链接,然后返回停留在隐私弹窗界面。

  • 3、查看是否有触发 不符合安全整改 的问题(测试人员)

    • 查看对应项目的crash缺陷列表:

      • 出现"未接受隐私弹窗就初始化SDK"相关的字样的bug,需要进行修正,禁止在未同意前初始化第三方SDK
      • 出现 "获取了软件安装列表"相关的字样的bug,需要进行修正,禁止获取设备软件安装列表;
      • 出现 "用户未同意隐私协议,主线程获取设备信息:"相关的字样的bug,需要进行修正,用户未统一前,禁止获取设备信息
      • 出现 ""主线程接口请求""相关的字样的bug,需要进行修正,禁止在主线程进行网络请求
      • 出现 "application接口请求"相关的字样的bug,不一定需要进行修正,由开发进一步确认即可。
      • 若用户未点击同意前,代理出现http请求,请反馈开发进行修改,说明有遗漏拦截的地方
      • 若无以上问题,说明验证通过。
  • 4、查看是否有触发 不符合安全整改 的问题(开发人员)

    • 查看logcat信息日志,过滤"Aspect"字样:

      • 出现 "未接受隐私弹窗就初始化SDK"相关的字样的bug,需要进行修正,禁止在未同意前初始化第三方SDK
      • 出现 "获取了软件安装列表"相关的字样的bug,需要进行修正,禁止获取设备软件安装列表;
      • 出现 "用户未同意隐私协议,主线程获取设备信息:"相关的字样的bug,需要进行修正,用户未统一前,禁止获取设备信息
      • 出现 ""主线程接口请求""相关的字样的bug,需要进行修正,禁止在主线程进行网络请求
      • 出现 "application接口请求"相关的字样的bug,不一定需要进行修正,由开发进一步确认即可。
      • 若用户未点击同意前,代理出现http请求,需要进行修改,说明有遗漏拦截的地方
      • 若无以上问题,说明验证通过。
  • 5、 核心hook api如下:

`package com.meiyou.app.aspectj;

@Aspect public class AspectjUtil {

java 复制代码
public static final String TAG = "AspectjUtil";

/**
 * 第三方SDK包含:
 * 友盟、bugly、QQ、微博、微信、支付宝、
 * 淘宝百川、七鱼、腾讯IM、
 * 阿里云播放器、穿山甲、腾讯X5、
 * 阿里云、高德地图、OPPO PUSH、VIVIO PUSH、J PUSH、HUAWEI PUSH、MI PUSH、
 * 腾讯短视频;
 * 美摄SDK
 * @param joinPoint
 * @return
 * @throws Throwable
 */
@Around("(call(* com.baichuan.nb_trade.core.AlibcTradeSDK.asyncInit(..))) || " +    //百川sdk
        "(call(* com.tencent.smtt.sdk.QbSdk.initX5Environment(..))) || " +          //X5
        "(call(* com.meiyou.framework.ui.widgets.pulltorefreshview.CustomWebView.init(..))) || " + //X5 webview
        "(call(* com.tencent.bugly.crashreport.CrashReport.initCrashReport(..))) || " +            //Bugly
        "(call(* com.umeng.commonsdk.UMConfigure.init(..))) || " +      //友盟SDK
        "(call(* com.meiyou.framework.share.SocialService.prepare(..))) || " +  //分享SDK
        "(call(* com.qiyukf.unicorn.api.Unicorn.init(..))) || " +               //七鱼SDK
        "(call(* com.bytedance.sdk.openadsdk.TTAdSdk.init(..))) || " +          //穿山甲SDK
        "(call(* com.tencent.imsdk.TIMManager.init(..))) || " +                 //腾讯IM SDK
        "(call(* com.amap.api.location.AMapLocationClient.startLocation(..))) || " +  //高德SDK
        "(call(* com.meiyou.pushsdk.PushSDK.init(..))) || " +
        "(call(* com.tencent.ugc.TXUGCBase.setLicence(..))) || " +              //腾讯视频SDK
        "(call(* com.meicam.sdk.NvsStreamingContext.init(..))) " )
public Object handleSDKInit(ProceedingJoinPoint joinPoint) throws Throwable {
    try {
        if (!AspectjController.INSTANCE.isAcceptFirstStartApp()) {
            if(isTest()){
                //上报tapd,用于测试记录
                String line="",className="",methodName="";
                try {
                     line = joinPoint.getSourceLocation().toString();  //BbjInit:83
                      className = joinPoint.getSignature().getDeclaringType().getSimpleName();// TTTBASE
                     methodName = joinPoint.getSignature().getName(); //setLicence
                }catch (Exception ex){
                    ex.printStackTrace();
                }
                String message = "严重警告:未接受隐私弹窗就初始化SDK:"+line+"->"+className+"->"+methodName;
                LogUtils.e(TAG, message);
                ToastUtils.showToast(MeetyouFramework.getContext(), message);
                sendToTapd(message,3);
                if(isTest() && joinPoint.getTarget()!=null &&  joinPoint.getSignature()!=null){
                    //打印logcat,用于后台埋点
                    String name = joinPoint.getSignature().getName();
                    String classSimpleName =  joinPoint.getTarget().getClass().getSimpleName();
                    AspectjLogController.INSTANCE.handleThirdSDKReport(
                            classSimpleName,name,"");
                }
            }
        }
    } catch (Exception e) {
    }
    return joinPoint.proceed();
}

@Around("(call(* android.content.pm.PackageManager.getInstalledPackages(..))) || "+
        "(call(* android.content.pm.PackageManager.getInstalledPackagesAsUser(..))) || " +
        "(call(* android.content.pm.PackageManager.getInstalledApplications(..)))")
public Object handleGlobalGetInstalledPackages(ProceedingJoinPoint joinPoint) throws Throwable {
    try {
        //上报tapd,用于测试记录
        if(isTest()){
            String line="",className="",methodName="";
            try {
                line = joinPoint.getSourceLocation().toString();  //BbjInit:83
                className = joinPoint.getSignature().getDeclaringType().getSimpleName();// TTTBASE
                methodName = joinPoint.getSignature().getName(); //setLicence
            }catch (Exception ex){
                ex.printStackTrace();
            }
            String message = "严重警告:获取了软件安装列表:"+line+"->"+className+"->"+methodName;
            LogUtils.e(TAG, message);
            ToastUtils.showToast(MeetyouFramework.getContext(), message);
            sendToTapd(message,3);
        }

        //打印logcat,用于后台埋点
        try {
            if(isTest()&& joinPoint.getTarget()!=null &&  joinPoint.getSignature()!=null){
                String name = joinPoint.getSignature().getName();
                String classSimpleName =  joinPoint.getTarget().getClass().getSimpleName();
                AspectjLogController.INSTANCE.handleLogReport(classSimpleName,name,"");
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }
        return joinPoint.proceed();
    } catch (Exception e) {
    }
    return null;
}

@Around("call(* android.content.Context.getSystemService(..))")
public Object location(ProceedingJoinPoint joinPoint) throws Throwable {
    try {
        Object[] args = joinPoint.getArgs();
        if(args!=null && isTest()){
            for (Object arg : args) {
                if (StringUtils.equalsIgnoreCase("location",arg.toString())) {
                    LogUtils.d(TAG, "arg = " + arg.toString());
                    String className = joinPoint.getSignature().getName();
                    LogUtils.e(TAG, "className = " + className);
                    LogUtils.e(TAG, "获取了用户的位置信息");
                    String message = Log.getStackTraceString(new Throwable());
                    AspectjLogController.INSTANCE.handleLocationReport(className,className,message);
                    LogUtils.d(TAG,message);
                }
            }
        }
        return joinPoint.proceed();//joinPoint.proceed();
    } catch (Exception e) {
        //e.printStackTrace();
    }
    return null;
}

@Around("(call(* com.amap.api.location.AMapLocationClient.startLocation(..))) ")
public Object handleGlobalLocation(ProceedingJoinPoint joinPoint) throws Throwable {
    try {
        //打印logcat,用于后台埋点
        if(isTest()&& joinPoint.getTarget()!=null &&  joinPoint.getSignature()!=null){
            String name = joinPoint.getSignature().getName();
            String classSimpleName =  joinPoint.getTarget().getClass().getSimpleName();
            AspectjLogController.INSTANCE.handleLocationReport(classSimpleName,name,"");
        }
    }catch (Exception ex){
        ex.printStackTrace();
    }
    return joinPoint.proceed();
}
@Around("(call(* android.content.ContentResolver.query(..))) ")
public Object handleGlobalContentResolver(ProceedingJoinPoint joinPoint) throws Throwable {
    try {
        try {
            if(isTest()&& joinPoint.getTarget()!=null &&  joinPoint.getSignature()!=null){
                String arg0 = null;
                if(joinPoint.getArgs()!=null && joinPoint.getArgs().length>0){
                    arg0 = joinPoint.getArgs()[0].toString();//content://call_log/calls
                }
                String name = joinPoint.getSignature().getName();
                String classSimpleName =  joinPoint.getTarget().getClass().getSimpleName();
                AspectjLogController.INSTANCE.handleContentProviderReport(arg0,classSimpleName,name,"");
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }
        return joinPoint.proceed();
    } catch (Exception e) {
    }
    return null;
}

/**
 * 禁止百川SDK获取应用列表
 * @param joinPoint
 * @return
 * @throws Throwable
 */
@Around("call(* com.alibaba.alibclinkpartner.smartlink.manager.ALSLAppCheckManager.getInstallAppList(..))")
public Object handleBaichuanGetInstalledPacakges(ProceedingJoinPoint joinPoint) throws Throwable {
    try {
        LogUtils.e(TAG,"捕获到百川获取应用列表,拦截它并反馈new ArrayList()");
        return new ArrayList<>();
    } catch (Exception e) {
    }
    return null;
}

//@Around("call(* android.app.ActivityManager.getRunningAppProcesses(..))")
@Around("execution(* com.alibaba.analytics.a.a.c())")
public Object handleAlibabaGetRunningAppProcess(ProceedingJoinPoint joinPoint) {
    try {
        //禁用百川定频获取,强制返回前后台标识;
        /*
        if(isHitForbiddenMethod(joinPoint,"getRunningAppProcesses","ActivityManager","com.alibaba.analytics")){
            return null;
        }
        return joinPoint.proceed();*/
        return MeetyouWatcher.getInstance().getAppFrontBackWatcher().isAppAtBg();
    }catch (Exception ex){
        ex.printStackTrace();
    } catch (Throwable throwable) {
        //throwable.printStackTrace();
    }
    return false;
}

private boolean isAppAtBg(){
    if(MeetyouWatcher.getInstance().getAppFrontBackWatcher()!=null
            && MeetyouWatcher.getInstance().getAppFrontBackWatcher().isAppAtBgMemory()){
        return true;
    }
    return false;
}
/**
 * AndroidQ适配
 * 修复友盟android 10 因为权限无法上报的问题;
 * <p>
 * 以及 工信部整改需求:未点击同意前,不允许获取设备信息
 * * https://www.tapd.cn/21039721/prong/stories/view/1121039721001042893
 * * @param joinPoint
 *
 * @param joinPoint
 * @return
 * @throws Throwable
 */
@Around("(call(* android.telephony.TelephonyManager.*(..))) || " +
        "(call(* android.net.wifi.WifiInfo.*(..))) || " +
        "(call(* java.net.NetworkInterface.getHardwareAddress*(..))) || " +
        "(call(* com.meiyou.sdk.core.UniqueIdUtils.getDeviceInfo(..))) || " +
        "(call(* com.meiyou.sdk.core.DeviceUtils.getProvidersIMSI(..))) || " +
        "(call(* com.meiyou.sdk.core.DeviceUtils.getPhoneOnlyKey(..))) || " +
        "(call(* com.meiyou.detector.functionlality.Telephony.getImei(..))) || " +
        "(call(* com.meiyou.detector.functionlality.Telephony.getImeiX(..))) || " +
        "(call(* com.meiyou.detector.functionlality.Telephony.getImsi(..))) || " +
        "(call(* com.meiyou.detector.functionlality.Telephony.getNetworkOperator(..))) || " +
        "(call(* com.meiyou.detector.functionlality.Telephony.getSimStatus(..))) || " +
        "(call(* com.meiyou.detector.functionlality.Telephony.getPhoneNumber(..))) || " +
        "(call(* com.umeng.commonsdk.statistics.common.DeviceConfig.getAndroidId(..))) || " +
        "(call(* com.umeng.commonsdk.statistics.common.DeviceConfig.getCPU(..))) || " +
        "(call(* com.umeng.commonsdk.statistics.common.DeviceConfig.getDeviceIdForGeneral(..))) || " +
        "(call(* com.umeng.commonsdk.statistics.common.DeviceConfig.getDeviceIdForBox(..))) || " +
        "(call(* com.umeng.commonsdk.statistics.common.DeviceConfig.getGPU(..))) || " +
        "(call(* com.umeng.commonsdk.statistics.common.DeviceConfig.getImei(..))) || " +
        "(call(* com.umeng.commonsdk.statistics.common.DeviceConfig.getIMEI(..))) || " +
        "(call(* com.umeng.commonsdk.statistics.common.DeviceConfig.getImeiNew(..))) || " +
        "(call(* com.umeng.commonsdk.statistics.common.DeviceConfig.getImsi(..))) || " +
        "(call(* com.umeng.commonsdk.statistics.common.DeviceConfig.getMacBySystemInterface(..))) || " +
        "(call(* com.umeng.commonsdk.statistics.common.DeviceConfig.getMacByJavaAPI(..))) || " +
        "(call(* com.umeng.commonsdk.statistics.common.DeviceConfig.getMacShell(..))) || " +
        "(call(* com.meiyou.common.apm.controller.ApmAgent.start(..))) || " +
        "(call(* com.meiyou.detector.DetectorManager.getDetectionInfoWithSign(..))) || " +
        "(call(* com.meiyou.framework.util.ChannelUtil.getStatisticInfo(..)))")
public Object handleGlobalGetDeviceInfo(ProceedingJoinPoint joinPoint) {
    try {


        //android 10无法获取imei
        if(Build.VERSION.SDK_INT >= 29 ){
            if(isHitForbiddenMethod(joinPoint,"getDeviceId","TelephonyManager","")){
                return null;
            }
        }
        //禁止小米SDK在后台的时候获取SSID
        if(isHitForbiddenMethodWhenBg(joinPoint,"getSSID","WifiInfo","com.xiaomi.push")){
            return null;
        }
        //禁止七鱼SDK在后台的时候获取IMEI
        if(isHitForbiddenMethodWhenBg(joinPoint,"getDeviceId","TelephonyManager","com.qiyukf")){
            return null;
        }

        if (!AspectjController.INSTANCE.isAcceptFirstStartApp()) {
            if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
                LogUtils.e(TAG, "线程中获取设备信息,因用户未同意隐私协议,已被拦截等待");
                while (true) {
                    Thread.sleep(50);
                    if (AspectjController.INSTANCE.isAcceptFirstStartApp()) {
                        break;
                    }
                }
                LogUtils.e(TAG, "线程中用户已同意隐私协议,释放拦截");
            } else {
                if (joinPoint.getSourceLocation().toString().contains("AppUtil")) {
                    return null;
                }
                try {
                    if (isTest()) {
                        String line="",className="",methodName="";
                        try {
                            line = joinPoint.getSourceLocation().toString();  //BbjInit:83
                            className = joinPoint.getSignature().getDeclaringType().getSimpleName();// TTTBASE
                            methodName = joinPoint.getSignature().getName(); //setLicence
                        }catch (Exception ex){
                            ex.printStackTrace();
                        }
                        String message = "严重警告:用户未同意隐私协议,主线程获取设备信息:"+line+"->"+className+"->"+methodName;
                        LogUtils.e(TAG, message);
                        ToastUtils.showToast(MeetyouFramework.getContext(), message);
                        sendToTapd(message,3);
                    }
                }catch (Exception ex){
                    ex.printStackTrace();
                }
                try {
                    if(isTest() && joinPoint.getTarget()!=null && joinPoint.getSignature()!=null){
                        String name = joinPoint.getSignature().getName();
                        String classSimpleName =  joinPoint.getTarget().getClass().getSimpleName();
                        AspectjLogController.INSTANCE.handleLogReport(classSimpleName,name,"");
                    }
                }catch (Exception ex){
                    ex.printStackTrace();
                }
            }
        } else {
            LogUtils.i(TAG, "用户已同意隐私协议,可正常获取设备信息");
            try {
                if(isAppAtBg()  && joinPoint.getSignature()!=null){
                    String name = joinPoint.getSignature().getName();
                    String classSimpleName = "";
                    if(joinPoint.getTarget()!=null){
                        classSimpleName = joinPoint.getTarget().getClass().getSimpleName();
                    }
                    if(AspectjLogController.INSTANCE.isHitDeviceInfo(name+"",classSimpleName+"")){
                        LogUtils.i(TAG,"后台命中获取设备信息methodname:"+name+" className:"+classSimpleName+" 返回null");
                        return null;
                    }
                }
                if(isTest() && joinPoint.getTarget()!=null && joinPoint.getSignature()!=null){
                    String name = joinPoint.getSignature().getName();
                    String classSimpleName =  joinPoint.getTarget().getClass().getSimpleName();
                    AspectjLogController.INSTANCE.handleLogReport(classSimpleName,name,"");
                }
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
        return joinPoint.proceed();
    } catch (Exception e) {
        //e.printStackTrace();
    } catch (Throwable throwable) {
        //throwable.printStackTrace();
    }
    return null;
}

private boolean isHitForbiddenMethod(ProceedingJoinPoint joinPoint,String methodName,String classsimpleName,String stack){
    try {
        try {
            if(joinPoint.getTarget()!=null && joinPoint.getSignature()!=null){
                String name = joinPoint.getSignature().getName();
                String classSimpleName =  joinPoint.getTarget().getClass().getSimpleName();
                if(StringUtils.isNull(methodName)){
                    if((classsimpleName.equalsIgnoreCase(classSimpleName))){
                        if(StringUtils.isNull(stack)){
                            return true;
                        }
                        if(getStackLog().contains(stack)){
                            LogUtils.e(TAG,"禁用"+stack+"获取获取设备信息:"+methodName+" 直接return "+classSimpleName);
                            return true;
                        }
                    }
                }else{
                    if((methodName.equalsIgnoreCase(name) && classsimpleName.equalsIgnoreCase(classSimpleName))){
                        if(StringUtils.isNull(stack)){
                            return true;
                        }
                        if(getStackLog().contains(stack)){
                            LogUtils.e(TAG,"禁用"+stack+"获取获取设备信息:"+methodName+" 直接return "+classSimpleName);
                            return true;
                        }
                    }
                }

            }
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }catch (Exception ex){
        ex.printStackTrace();
    }
    return false;
}

private boolean isHitForbiddenMethodWhenBg(ProceedingJoinPoint joinPoint,String methodName,String classsimpleName,String stack){
    try {
        try {
            if(MeetyouWatcher.getInstance().getAppFrontBackWatcher()!=null
                    && MeetyouWatcher.getInstance().getAppFrontBackWatcher().isAppAtBgMemory()
                    &&  joinPoint.getTarget()!=null && joinPoint.getSignature()!=null){
                String name = joinPoint.getSignature().getName();
                String classSimpleName =  joinPoint.getTarget().getClass().getSimpleName();
                if(StringUtils.isNull(methodName)){
                    if((classsimpleName.equalsIgnoreCase(classSimpleName))){
                        if(getStackLog().contains(stack)){
                            LogUtils.e(TAG,"在后台时,禁用"+stack+"获取获取设备信息:"+methodName+" 直接return "+classSimpleName);
                            return true;
                        }
                    }
                }else{
                    if((methodName.equalsIgnoreCase(name) && classsimpleName.equalsIgnoreCase(classSimpleName))){
                        if(getStackLog().contains(stack)){
                            LogUtils.e(TAG,"在后台时,禁用"+stack+"获取获取设备信息:"+methodName+" 直接return "+classSimpleName);
                            return true;
                        }
                    }
                }

            }
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }catch (Exception ex){
        ex.printStackTrace();
    }
    return false;
}

@Around("(execution(*  com.tencent.smtt.utils.b.d(..))) || " +
        "(execution(*  com.tencent.smtt.utils.b.f(..))) || " +
        "(execution(*  com.tencent.smtt.utils.b.e(..))) || " +
        "(execution(*  com.tencent.smtt.utils.b.g(..)))")
public Object handleX5GetDeviceInfo(ProceedingJoinPoint joinPoint) throws Throwable {
    try {
        if (!AspectjController.INSTANCE.isAcceptFirstStartApp()) {
            LogUtils.e(TAG, "用户未同意隐私协议,禁止com.tencent.smtt等方法执行获取设备信息");
            return null;
        } else {
            return joinPoint.proceed();
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return joinPoint.proceed();
}

/**
 * 工信部整改需求:未同意前,不允许请求网络
 * https://www.tapd.cn/21039721/prong/stories/view/1121039721001042893
 *
 * @param joinPoint
 * @throws Throwable
 */
@Around("(call(* okhttp3.OkHttpClient.newCall(..))) || (call(* okhttp3.Call.execute(..)))")
public Object handleOkHttp(ProceedingJoinPoint joinPoint) throws Throwable {
    try {
        if (!AspectjController.INSTANCE.isAcceptFirstStartApp()) {
            if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
                LogUtils.e(TAG, "线程中触发接口,因用户未同意隐私协议,已被拦截等待");
                while (true) {
                    Thread.sleep(50);
                    if (AspectjController.INSTANCE.isAcceptFirstStartApp()) {
                        break;
                    }
                }
                LogUtils.e(TAG, "用户已同意隐私协议,释放拦截接口等待");
            }
        } else {
            LogUtils.i(TAG, "用户已同意隐私协议,可正常进行网络请求");
        }
        //不是正式包,但是正式环境
        if(!AspectjController.INSTANCE.isProductApk()){
            if(isTest()){
                String url = getRequestUrl(joinPoint);
                if(!StringUtils.isNull(url) && url.contains("test-") || url.contains("yf-")){
                    //正式环境请求了test-
                    try {
                        AspectjLogController.INSTANCE.handleHttpWrongReport("","",url);
                    }catch (Exception ex){
                        ex.printStackTrace();
                    }
                }
            }
        }
        if(isTest()){
            final String url = getRequestUrl(joinPoint);
            try {
                    AspectjLogController.INSTANCE.handleHttpReport("","",url);
            }catch (Exception ex){
                ex.printStackTrace();
            }

            //检测主线程http请求
            /*if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
                if (!AspectjController.INSTANCE.isWhiteUrl(url)
                         && !url.contains("doraemon.xiaojukeji.com")) {
                    // 解析请求地址
                    if (!StringUtils.isNull(url)) {
                        Uri uri = Uri.parse(url);
                        String message = uri.getPath()+ ":主线程接口请求:"+url;
                        LogUtils.e(TAG, message);
                        ToastUtils.showToast(MeetyouFramework.getContext(), message);
                        sendToTapd(message,3);
                    }
                }
            }*/
            //检测application 里的网络请求
            if(AspectjController.INSTANCE.isReportApplicationHttp()){
                if (!AspectjController.INSTANCE.isWhiteUrl(url)&& !url.contains("doraemon.xiaojukeji.com")) {
                    if (MeetyouWatcher.getInstance().getActvityWatcher()!=null
                            && MeetyouWatcher.getInstance().getActvityWatcher().getTopActivity()==null) {
                        new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                if (MeetyouWatcher.getInstance().getActvityWatcher()!=null && MeetyouWatcher.getInstance().getActvityWatcher().getTopActivity()==null) {
                                    if (!StringUtils.isNull(url)) {
                                        Uri uri = Uri.parse(url);
                                        String urlLog = uri.getScheme()+"://"+uri.getHost()+uri.getPath();//放服务端虑重,移除query
                                        String message = uri.getPath()+ ":application接口请求:"+urlLog;
                                        sendToTapd(message, 3);
                                    }
                                }
                            }
                        },1000);
                    }
                }
            }
        }
        return joinPoint.proceed();
    } catch (Exception e) {
        // e.printStackTrace();
    } catch (Throwable throwable) {
        // throwable.printStackTrace();
    }
    return null;
}


/**
 * 解析获取请求地址
 */
public String getRequestUrl(ProceedingJoinPoint joinPoint) {
    String url = "";
    try {
        if (joinPoint != null && joinPoint.getTarget() != null) {
            if (joinPoint.getTarget() instanceof Call) {
                Call call = (Call) joinPoint.getTarget();
                url = call.request().url().toString();
                LogUtils.i(TAG, "Call url=" + url);
            }
            if (joinPoint.getTarget() instanceof OkHttpClient) {
                if (joinPoint.getArgs() != null
                        && joinPoint.getArgs().length > 0
                        && joinPoint.getArgs()[0] instanceof Request) {
                    url = ((Request) joinPoint.getArgs()[0]).url().toString();
                    LogUtils.i(TAG, "OkHttpClient url=" + url);
                }
            }
        }
    } catch (Exception e) {
    }
    return url;
}

/**
 * 发送到tapd
 */
public void sendToTapd(String errorMsg, int infoType) {
    try {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("errorMsg", errorMsg);
        jsonObject.put("stack", getStackLog());
        AspectjController.INSTANCE.sendToTapd(jsonObject.toString(), infoType);
    } catch (Exception e) {

    }
}

private String  getStackLog(){
    try {
        String str = Log.getStackTraceString(new Throwable());
        if(str.length()>800){
            return str.substring(0,800);
        }else{
            return str;
        }
    }catch (Exception ex){
        ex.printStackTrace();
    }

    return "";
}

private boolean isTest() {
    return ConfigManager.from(MeetyouFramework.getContext()).isDebug()
            || ConfigManager.from(MeetyouFramework.getContext()).isTest();
}

} `

参考资料

相关推荐
辻戋1 小时前
从零实现React Scheduler调度器
前端·react.js·前端框架
徐同保1 小时前
使用yarn@4.6.0装包,项目是react+vite搭建的,项目无法启动,报错:
前端·react.js·前端框架
Qrun2 小时前
Windows11安装nvm管理node多版本
前端·vscode·react.js·ajax·npm·html5
中国lanwp2 小时前
全局 npm config 与多环境配置
前端·npm·node.js
JELEE.3 小时前
Django登录注册完整代码(图片、邮箱验证、加密)
前端·javascript·后端·python·django·bootstrap·jquery
TeleostNaCl5 小时前
解决 Chrome 无法访问网页但无痕模式下可以访问该网页 的问题
前端·网络·chrome·windows·经验分享
前端大卫7 小时前
为什么 React 中的 key 不能用索引?
前端
你的人类朋友7 小时前
【Node】手动归还主线程控制权:解决 Node.js 阻塞的一个思路
前端·后端·node.js
小李小李不讲道理9 小时前
「Ant Design 组件库探索」五:Tabs组件
前端·react.js·ant design
毕设十刻9 小时前
基于Vue的学分预警系统98k51(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js