//
// Created by Fear1ess on 2021/4/2.
//

#include "collect.h"
#include "wd_jni.h"
#include "utils.h"
#include <stdlib.h>
#include "wd_result.h"
#include "wdun.h"
#include <fcntl.h>
#include <fake_dlfcn.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/sysinfo.h>
#include <sys/statfs.h>
#include <sys/stat.h>
#include "openssl/md5.h"
#include <arpa/inet.h>
#include <sys/syscall.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <zlib.h>
#include "https.h"
#include "aesencode.h"

#define WD_COLLECT "wd_collect"


extern jobject g_app_context;
extern struct wd_funcs g_funcs;

IMPORTWDSYSCALL

cJSON *collect_init() {
    return cJSON_CreateObject();
}

jstring do_collect(JNIEnv* env) {
    cJSON* json =  collect_init();
    struct timespec ts = {0};
    double start_time = 0, end_time = 0;
    if(WDSYSCALL(SYS_clock_gettime, CLOCK_REALTIME, &ts) == 0) {
        start_time = ts.tv_nsec / 1000000  + ts.tv_sec * 1000ULL;
    }
    //添加sdk版本号
    cJSON_AddStringToObject(json, "sdk_ver", "1.0.0");
    collect_app_info(env, json);
    int permissions = collect_permissions(env, json);
    collect_imei(env, json);
    collect_android_id(env, json);
    collect_mac_addr(env, json);
    collect_build_info(env, json);
    collect_prop_info(env, json);
    collect_settings_info(env, json);
    collect_display_info(env, json);
    collect_proxy_info(env, json);
    collect_audio_info(env, json);
    collect_camera_info(env, json);
    collect_battery_info(env, json);
    collect_env(env, json);
    collect_libs_info(env, json);
    collect_cpu_info(env, json);
    collect_sensor_info(env, json);
    collect_mem_info(env, json);
    collect_network_info(env, json);
    collect_user_agent(env, json);
    if((permissions & PERMISSION_ACCESS_FINE_LOCATION) && (permissions & PERMISSION_ACCESS_COARSE_LOCATION)) {
        collect_location_info(env, json);
    }
    if((permissions & PERMISSION_READ_PHONE_STATE) && (permissions & PERMISSION_ACCESS_COARSE_LOCATION)) {
        collect_cell_info(env, json);
    }
    collect_system_id(env, json);
    collect_time_info(env, json);
    collect_common_app(env, json);
    collect_installed_apps(env, json);
    collect_risk_info(env, json);

    if(WDSYSCALL(SYS_clock_gettime, CLOCK_REALTIME, &ts) == 0) {
        end_time = (double)ts.tv_nsec / 1000000  + (double)ts.tv_sec * 1000ULL;
    }

    char srt[32] = {0};
    snprintf(srt, 31, "%.2lf", end_time - start_time);
    cJSON_AddStringToObject(json, "srt", srt);

    const char* data = cJSON_Print(json);

    const char* send_data = cJSON_PrintUnformatted(json);
    size_t data_len = strlen(send_data);

    //zlib compress
    void* z_lib = fake_dlopen("libz.so", 0);
    if(z_lib) {
        u_long (*compressBound_func) (u_long) = fake_dlsym(z_lib, "compressBound");
        int (*compress_func) (unsigned char* dest, u_long* p_dest_len, const unsigned char* src, u_long src_len) =
                fake_dlsym(z_lib, "compress");
        if(compress && compressBound_func) {
            u_long bound = compressBound_func(data_len);
            unsigned char* compressed_data = malloc(bound);
            int res = compress_func(compressed_data, &bound, send_data, data_len);
            if(res == Z_OK) {
                //加密
                char aes_key[17] = {0}, aes_iv[17] = {0};
                char* encoded_data;
                size_t encoded_len = 0;
                aes_Encoder_Base64(compressed_data, bound, &encoded_data, &encoded_len, aes_key, aes_iv);

                //https
                http_PostConnect(env, "https://192.168.7.103:6666", encoded_data, encoded_len);
            }
        }
    }

    jstring data_jstr = (*env)->NewStringUTF(env, data);
    return data_jstr;
}

void collect_app_info(JNIEnv* env, cJSON *json) {
    if((*env)->PushLocalFrame(env, 20) != JNI_OK) {
        loge(WD_COLLECT, "%s", "collect_app_info func call env->pushLocalFrame failed!");
        return;
    }

    //package_name
    jobject pkgName = wdCallObjectMethod(env, g_app_context, "getPackageName", "()Ljava/lang/String;");
    addJniStringToJson(env, json, "package_name", pkgName);

    //get packagemanager and pkginfo
    jobject pkgManager = wdCallObjectMethod(env, g_app_context, "getPackageManager", "()Landroid/content/pm/PackageManager;");
    jobject pkgInfo = wdCallObjectMethod(env, pkgManager, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;", pkgName, 0x40);


    //last update time
    jlong lastUpdateTime = wdGetLongField(env, pkgInfo, "lastUpdateTime", "J");
    cJSON_AddNumberToObject(json, "last_update_time", lastUpdateTime);

    //first install time
    jlong firstInstallTime = wdGetLongField(env, pkgInfo, "firstInstallTime", "J");
    cJSON_AddNumberToObject(json, "first_install_time", firstInstallTime);

    //version code
    jint versionCode = wdGetIntField(env, pkgInfo, "versionCode", "I");
    cJSON_AddNumberToObject(json, "ver_code", versionCode);

    //version name
    jobject versionName = wdGetObjectField(env, pkgInfo, "versionName", "Ljava/lang/String;");
    addJniStringToJson(env, json, "version_name", versionName);

    //signature's sha256
    jobject signatures = wdGetObjectField(env, pkgInfo, "signatures", "[Landroid/content/pm/Signature;");
    jobject sig_item0 = (*env)->GetObjectArrayElement(env, signatures, 0);
    jobject signature = wdCallObjectMethod(env, sig_item0, "toCharsString", "()Ljava/lang/String;");
    char* sha256 = (char*)malloc(64 + 1);
    getJniStringSha256(env, signature, sha256);
    cJSON_AddStringToObject(json, "signature_sha256", sha256);

    //app label
    jobject aiInfo = wdCallObjectMethod(env, g_app_context, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
    jobject label = wdCallObjectMethod(env, pkgManager, "getApplicationLabel", "(Landroid/content/pm/ApplicationInfo;)Ljava/lang/CharSequence;", aiInfo);
    addJniStringToJson(env, json, "app_label", label);

    //app_debug & app_backup
    jint flags = wdGetIntField(env, aiInfo, "flags", "I");
    cJSON_AddNumberToObject(json, "app_debug", (flags & (1 << 1)) ? 1 : 0);
    cJSON_AddNumberToObject(json, "app_backup", (flags & (1 << 15)) ? 1 : 0);

    //launcher
    jstring action_jstr = (*env)->NewStringUTF(env, "android.intent.action.MAIN");
    jstring category_jstr = (*env)->NewStringUTF(env, "android.intent.category.HOME");
    jobject intent = wdNewObject(env, "android/content/Intent", "(Ljava/lang/String;)V", action_jstr);
    wdCallObjectMethod(env, intent, "addCategory", "(Ljava/lang/String;)Landroid/content/Intent;", category_jstr);
    jobject ri = wdCallObjectMethod(env, pkgManager, "resolveActivity",
            "(Landroid/content/Intent;I)Landroid/content/pm/ResolveInfo;", intent, 0);
    if(ri != NULL) {
        jobject ai = wdGetObjectField(env, ri, "activityInfo", "Landroid/content/pm/ActivityInfo;");
        jstring launcher_jstr = wdGetObjectField(env, ai, "packageName", "Ljava/lang/String;");
        addJniStringToJson(env, json, "launcher", launcher_jstr);
    }

    //uid
    jint uid = wdGetIntField(env, aiInfo, "uid", "I");
    cJSON_AddNumberToObject(json, "uid", uid);

    //pid
    int pid = WDSYSCALL(SYS_getpid);
    int ppid = WDSYSCALL(SYS_getppid);
    cJSON_AddNumberToObject(json, "pid", pid);
    cJSON_AddNumberToObject(json, "ppid", ppid);
    FILE* fp = fopen("/proc/self/status", "r");
    if(fp) {
        char line[128];
        while(fgets(line, 127, fp)) {
            if(strstr(line, "TracerPid")) {
                int tracer_pid = -1;
                sscanf(line, "%*s%d", &tracer_pid);
                cJSON_AddNumberToObject(json, "tracer_pid", tracer_pid);
                break;
            }
        }
        fclose(fp);
    }

    //apk_path
    jstring apk_path_jstr = wdCallObjectMethod(env, g_app_context, "getPackageResourcePath", "()Ljava/lang/String;");
    if(apk_path_jstr != NULL) {
        const char* path = (*env)->GetStringUTFChars(env, apk_path_jstr, NULL);
        cJSON_AddStringToObject(json, "apk_path", path);

        //apk_size
        struct stat st;
        if(WDSYSCALL(WD_SYS_fstatat, AT_FDCWD, path, &st, 0) == 0) {
            cJSON_AddNumberToObject(json, "apk_size", st.st_size);
        }
        (*env)->ReleaseStringUTFChars(env, apk_path_jstr, path);
        (*env)->DeleteLocalRef(env, apk_path_jstr);
   }

    logd(WD_COLLECT, "%s", "collect app info finished...");
    (*env)->PopLocalFrame(env, NULL);
}

int collect_permissions(JNIEnv* env, cJSON* json) {

    int value = 0;
    const char* permissions[] = {
            "android.permission.READ_EXTERNAL_STORAGE",
            "android.permission.WRITE_EXTERNAL_STORAGE",
            "android.permission.READ_PHONE_STATE",
            "android.permission.ACCESS_NETWORK_STATE",
            "android.permission.ACCESS_WIFI_STATE",
            "android.permission.ACCESS_COARSE_LOCATION",
            "android.permission.ACCESS_FINE_LOCATION"
    };
    for(int i = 0; i < sizeof(permissions)/sizeof(const char*); ++i) {
        jstring str = (*env)->NewStringUTF(env, permissions[i]);
        if(wdCallIntMethod(env, g_app_context, "checkSelfPermission", "(Ljava/lang/String;)I", str) == 0) {
            value |= (1 << i);
        }
        (*env)->DeleteLocalRef(env, str);
    }

    cJSON_AddNumberToObject(json, "permissions", value);

    return value;
}

void collect_imei(JNIEnv *env, cJSON *json) {
    //get imei from telephonymanager
    jstring phone_str = (*env)->NewStringUTF(env, "phone");
    jobject tm = wdCallObjectMethod(env, g_app_context, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;", phone_str);
    jobject imei_str = wdCallObjectMethod(env, tm, "getDeviceId", "()Ljava/lang/String;");
    addJniStringToJson(env, json, "imei", imei_str);
    (*env)->DeleteLocalRef(env, phone_str);
    (*env)->DeleteLocalRef(env, tm);
    (*env)->DeleteLocalRef(env, imei_str);

    logd(WD_COLLECT, "%s", "collect imei finished...");
}

void collect_android_id(JNIEnv* env, cJSON* json) {
    jstring key_str = (*env)->NewStringUTF(env, "android_id");
    jobject cr = wdCallObjectMethod(env, g_app_context, "getContentResolver", "()Landroid/content/ContentResolver;");
    jobject androidId_jstr = wdCallStaticObjectMethod(env, "android/provider/Settings$Secure", "getString",
            "(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;", cr, key_str);
    addJniStringToJson(env, json, "android_id", androidId_jstr);
    (*env)->DeleteLocalRef(env, key_str);
    (*env)->DeleteLocalRef(env, cr);
    (*env)->DeleteLocalRef(env, androidId_jstr);
    logd(WD_COLLECT, "%s", "collect android_id finished...");
}

void collect_mac_addr(JNIEnv *env, cJSON *json) {
    // //above android 11.0+ can't get mac by this method
    jstring name_str = (*env)->NewStringUTF(env, "wlan0");
    jobject ni = wdCallStaticObjectMethod(env, "java/net/NetworkInterface", "getByName", "(Ljava/lang/String;)Ljava/net/NetworkInterface;", name_str);
    if(ni == NULL) {
        goto return_label2;
    }
    jbyteArray mac_byteArr = wdCallObjectMethod(env, ni, "getHardwareAddress", "()[B");
    if(mac_byteArr == NULL) {
        cJSON_AddStringToObject(json, "mac_addr", "");
        goto return_label;
    }
    jsize size = (*env)->GetArrayLength(env, mac_byteArr);
    jbyte* bytes = (*env)->GetByteArrayElements(env, mac_byteArr, NULL);
    char* out = (char*) malloc(size * 3);
    bytes2Hex((const uint8_t*)bytes, out, size, 1);
    cJSON_AddStringToObject(json, "mac_addr", out);
    free(out);
    (*env)->ReleaseByteArrayElements(env, mac_byteArr, bytes, 0);
    (*env)->DeleteLocalRef(env, mac_byteArr);
    return_label:
    (*env)->DeleteLocalRef(env, ni);
    return_label2:
    (*env)->DeleteLocalRef(env, name_str);

    logd(WD_COLLECT, "%s", "collect mac_addr finished...");
}

void collect_build_info(JNIEnv *env, cJSON *json) {
    if((*env)->PushLocalFrame(env, 20) != JNI_OK) {
        loge(WD_COLLECT, "%s", "collect_build_info func call env->pushLocalFrame failed!");
        return;
    }
    const char* build_key[] = {"SERIAL", "BOARD", "BOOTLOADER", "CPU_ABI", "CPU_ABI2", "DEVICE", "DISPLAY", "FINGERPRINT", "HARDWARE",
                               "HOST", "ID", "MANUFACTURER", "MODEL", "PRODUCT", "BRAND"};
    cJSON* bi_json = cJSON_CreateObject();

    for(int i = 0; i < sizeof(build_key)/sizeof(const char*); ++i) {
        jobject jstr = wdGetStaticObjectField(env, "android/os/Build", build_key[i], "Ljava/lang/String;");
        addJniStringToJson(env, bi_json, build_key[i], jstr);
    }

    cJSON_AddItemToObject(json, "build", bi_json);
    (*env)->PopLocalFrame(env, NULL);

    logd(WD_COLLECT, "%s", "collect build_info finished...");
}

void collect_prop_info(JNIEnv* env, cJSON* json) {
    if(g_funcs.wd_system_property_get == NULL) return;
    cJSON* pi_json = cJSON_CreateObject();
    const char* prop_key[] = {"wifi.interface", "gsm.sim.state", "gsm.version.baseband", "gsm.version.ril-impl",
                              "gsm.current.phone-type", "gsm.operator.isroaming", "gsm.network.type", "persist.sys.timezone",
                              "init.svc.adbd", "ro.build.date.utc", "gsm.operator.alpha", "ro.opengles.version",
                              "ro.serialno"};
    char buf[64];
    for(int i = 0; i < sizeof(prop_key)/sizeof(const char*); ++i) {
        memset((void*)buf, 0, 64);
        g_funcs.wd_system_property_get(prop_key[i], buf);
        cJSON_AddStringToObject(pi_json, prop_key[i], buf);
    }
    cJSON_AddItemToObject(json, "prop", pi_json);
    logd(WD_COLLECT, "%s", "collect prop_info finished...");
}

void collect_settings_info(JNIEnv *env, cJSON *json) {
    const char* settings_secure_key[] = {"usb_mass_storage_enabled", "development_settings_enabled", "accessibility_enabled"};
    const char* settings_system_key[] = {"screen_brightness", "accelerometer_rotation", "screen_brightness_mode",
                                       "sound_effects_enabled", "screen_off_timeout"};
  //  const char* settings_global_key[] = {"lock_pattern_autolock", "lock_pattern_visible_pattern"};


    jobject cr = wdCallObjectMethod(env, g_app_context, "getContentResolver", "()Landroid/content/ContentResolver;");

    cJSON* si_json = cJSON_CreateObject();
    for(int i = 0; i < sizeof(settings_secure_key)/sizeof(const char*); ++i) {
        jstring key_str = (*env)->NewStringUTF(env, settings_secure_key[i]);

        jobject jstr = wdCallStaticObjectMethod(env, "android/provider/Settings$Secure", "getString",
                                                          "(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;", cr, key_str);
        addJniStringToJson(env, si_json, settings_secure_key[i], jstr);
        (*env)->DeleteLocalRef(env, key_str);
        (*env)->DeleteLocalRef(env, jstr);
    }
    /*
    for(int m = 0; m < sizeof(settings_global_key)/sizeof(const char*); ++m) {
        jstring key_str = (*env)->NewStringUTF(env, settings_global_key[m]);
        jint value = wdCallStaticIntMethod(env, "android/provider/Settings$Global", "getInt",
                                           "(Landroid/content/ContentResolver;Ljava/lang/String;)I", cr, key_str);
        cJSON_AddNumberToObject(si_json, settings_global_key[m], value);
        (*env)->DeleteLocalRef(env, key_str);
    }*/
    for(int j = 0; j < sizeof(settings_system_key)/sizeof(const char*); ++j) {
        jstring key_str = (*env)->NewStringUTF(env, settings_system_key[j]);
        jint value = wdCallStaticIntMethod(env, "android/provider/Settings$System", "getInt",
                                                "(Landroid/content/ContentResolver;Ljava/lang/String;)I", cr, key_str);
        cJSON_AddNumberToObject(si_json, settings_system_key[j], value);
        (*env)->DeleteLocalRef(env, key_str);
    }
    (*env)->DeleteLocalRef(env, cr);
    cJSON_AddItemToObject(json, "settings", si_json);
    logd(WD_COLLECT, "%s", "collect settings_info finished...");
}

void collect_display_info(JNIEnv *env, cJSON *json) {
    cJSON* di_json = cJSON_CreateObject();
    jobject rc = wdCallObjectMethod(env, g_app_context, "getResources", "()Landroid/content/res/Resources;");
    jobject dm = wdCallObjectMethod(env, rc, "getDisplayMetrics", "()Landroid/util/DisplayMetrics;");
    jint height = wdGetIntField(env, dm, "heightPixels", "I");
    jint width = wdGetIntField(env, dm, "widthPixels", "I");
    jint dpi = wdGetIntField(env, dm, "densityDpi", "I");
    cJSON_AddNumberToObject(di_json, "height", height);
    cJSON_AddNumberToObject(di_json, "width", width);
    cJSON_AddNumberToObject(di_json, "dpi", dpi);
    cJSON_AddItemToObject(json, "display", di_json);
    (*env)->DeleteLocalRef(env, rc);
    (*env)->DeleteLocalRef(env, dm);
    logd(WD_COLLECT, "%s", "collect display_info finished...");
}

void collect_proxy_info(JNIEnv *env, cJSON *json) {
    if(g_funcs.wd_system_property_get == NULL) return;
    cJSON* proxy_json = cJSON_CreateObject();
    const char* prop_key[] = {"http.proxyHost", "http.proxyPort"};
    char buf[32];
    for(int i = 0; i < sizeof(prop_key)/sizeof(const char*); ++i) {
        memset((void*)buf, 0, 32);
        g_funcs.wd_system_property_get(prop_key[i], buf);
        cJSON_AddStringToObject(proxy_json, prop_key[i], buf);
    }
    cJSON_AddItemToObject(json, "proxy", proxy_json);
    logd(WD_COLLECT, "%s", "collect proxy_info finished...");
}

void collect_audio_info(JNIEnv *env, cJSON *json) {
    cJSON* audio = cJSON_CreateObject();
    jobject audio_jstr = (*env)->NewStringUTF(env, "audio");
    jobject am = wdCallObjectMethod(env, g_app_context,"getSystemService",
                                                 "(Ljava/lang/String;)Ljava/lang/Object;", audio_jstr);
    if(am != NULL) {
        cJSON* volume_arr = cJSON_CreateArray();
        for(int i = 0; i < 6; ++i) {
            jint volume = wdCallIntMethod(env, am, "getStreamVolume", "(I)I", i);
            cJSON_AddItemToArray(volume_arr, cJSON_CreateNumber(volume));
        }
        cJSON_AddItemToObject(audio, "volume", volume_arr);
        jint ring_mode = wdCallIntMethod(env, am, "getRingerMode", "()I");
        cJSON_AddNumberToObject(audio, "ring_mode", ring_mode);
        (*env)->DeleteLocalRef(env, am);
        (*env)->DeleteLocalRef(env, audio_jstr);
    }
    cJSON_AddItemToObject(json, "audio", audio);
    logd(WD_COLLECT, "%s", "collect audio_info finished...");
}

// by文杰
void collect_camera_info(JNIEnv *env, cJSON *json) {

    cJSON* item_json = cJSON_CreateArray();
    //获取CameraManager对象
    jobject camera_Str=(*env)->NewStringUTF(env, "camera");
    jobject cameraManager_obj=wdCallObjectMethod(env, g_app_context,"getSystemService",
                                                 "(Ljava/lang/String;)Ljava/lang/Object;", camera_Str);
    jobjectArray cameraIdList_Obj=wdCallObjectMethod(env,cameraManager_obj,"getCameraIdList",
                                                     "()[Ljava/lang/String;");
    int count=(*env)->GetArrayLength(env,cameraIdList_Obj);
    logd(WD_COLLECT, "%d", count);
    //cJSON_AddNumberToObject(item_json,"count",count);
    //
    jobject SCALER_AVAILABLE_MAX_DIGITAL_ZOOM=wdGetStaticObjectField(env,"android/hardware/camera2/CameraCharacteristics"
            ,"SCALER_AVAILABLE_MAX_DIGITAL_ZOOM","Landroid/hardware/camera2/CameraCharacteristics$Key;");
    jobject SCALER_STREAM_CONFIGURATION_MAP =wdGetStaticObjectField(env,"android/hardware/camera2/CameraCharacteristics"
            ,"SCALER_STREAM_CONFIGURATION_MAP","Landroid/hardware/camera2/CameraCharacteristics$Key;"
    );
    jobject SENSOR_ORIENTATION =wdGetStaticObjectField(env,"android/hardware/camera2/CameraCharacteristics"
            ,"SENSOR_ORIENTATION","Landroid/hardware/camera2/CameraCharacteristics$Key;"
    );
    jobject CONTROL_AE_COMPENSATION_RANGE =wdGetStaticObjectField(env,"android/hardware/camera2/CameraCharacteristics"
            ,"CONTROL_AE_COMPENSATION_RANGE","Landroid/hardware/camera2/CameraCharacteristics$Key;"
    );
    jobject CONTROL_MAX_REGIONS_AE =wdGetStaticObjectField(env,"android/hardware/camera2/CameraCharacteristics"
            ,"CONTROL_MAX_REGIONS_AE","Landroid/hardware/camera2/CameraCharacteristics$Key;"
    );
    jobject CONTROL_MAX_REGIONS_AF =wdGetStaticObjectField(env,"android/hardware/camera2/CameraCharacteristics"
            ,"CONTROL_MAX_REGIONS_AF","Landroid/hardware/camera2/CameraCharacteristics$Key;"
    );
    jobject SENSOR_INFO_PHYSICAL_SIZE =wdGetStaticObjectField(env,"android/hardware/camera2/CameraCharacteristics"
            ,"SENSOR_INFO_PHYSICAL_SIZE","Landroid/hardware/camera2/CameraCharacteristics$Key;"
    );
    jobject SENSOR_INFO_PIXEL_ARRAY_SIZE =wdGetStaticObjectField(env,"android/hardware/camera2/CameraCharacteristics"
            ,"SENSOR_INFO_PIXEL_ARRAY_SIZE","Landroid/hardware/camera2/CameraCharacteristics$Key;"
    );

    //循环获取每个摄像头对应的信息
    for (int i = 0; i < count ; ++i) {
        cJSON* item_json_item = cJSON_CreateObject();
        //获取摄像头的序号
        jstring cameraId=(*env)->GetObjectArrayElement(env,cameraIdList_Obj,i);
        char* camera_str=(*env)->GetStringUTFChars(env,cameraId,0);
      //  cJSON_AddStringToObject(item_json,"cameraId",camera_str);
        logd(WD_COLLECT, "cameraId = %s",camera_str);
        cJSON_AddStringToObject(item_json_item,"cameraId",camera_str);

        jobject cameraCharacteristics=wdCallObjectMethod(env,cameraManager_obj,
                                                         "getCameraCharacteristics",
                                                         "(Ljava/lang/String;)Landroid/hardware/camera2/CameraCharacteristics;",cameraId);
        //获取最大的Zoom
        jobject maxZoom=wdCallObjectMethod(env,cameraCharacteristics,"get"
                ,"(Landroid/hardware/camera2/CameraCharacteristics$Key;)Ljava/lang/Object;",SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
        if(maxZoom!=NULL){
            float zoom=wdCallFloatMethod(env,maxZoom,"floatValue","()F");
            cJSON_AddNumberToObject(item_json_item,"maxZoom",zoom);
            logd(WD_COLLECT,"maxZoom= %lf",zoom);
        }
        //获取
        jobject maxPixel=wdCallObjectMethod(env,cameraCharacteristics,"get"
                ,"(Landroid/hardware/camera2/CameraCharacteristics$Key;)Ljava/lang/Object;",SCALER_STREAM_CONFIGURATION_MAP);
        if(maxPixel!=NULL){
            int format=256;
            jobjectArray outputSizes=wdCallObjectMethod(env,maxPixel,"getOutputSizes"
                    ,"(I)[Landroid/util/Size;",format);

            int len=(*env)->GetArrayLength(env,outputSizes);
            if(len>0){
                jobject obj=(*env)->GetObjectArrayElement(env,outputSizes,0);
                int maxPixel_Int=wdCallIntMethod(env,obj,"getWidth","()I")
                                 *wdCallIntMethod(env,obj,"getHeight","()I");
                cJSON_AddNumberToObject(item_json_item,"maxPixel",maxPixel_Int);
                logd(WD_COLLECT,"maxPixel= %d",maxPixel_Int);
                (*env)->DeleteLocalRef(env, obj);
            }
            (*env)->DeleteLocalRef(env, outputSizes);
        }
        //获取传感器方向
        jobject ori=wdCallObjectMethod(env,cameraCharacteristics,"get"
                ,"(Landroid/hardware/camera2/CameraCharacteristics$Key;)Ljava/lang/Object;",SENSOR_ORIENTATION);
        if(ori!=NULL){
            int ori_num=wdCallIntMethod(env,ori,"intValue","()I");
            cJSON_AddNumberToObject(item_json_item,"ori",ori_num);
            logd(WD_COLLECT,"ori= %d",ori_num);
        }
        //获取补偿范围
        jobject aeRange=wdCallObjectMethod(env,cameraCharacteristics,"get"
                ,"(Landroid/hardware/camera2/CameraCharacteristics$Key;)Ljava/lang/Object;",CONTROL_AE_COMPENSATION_RANGE);
        if(aeRange!=NULL){
            jobject lower=wdCallObjectMethod(env,aeRange,"getLower","()Ljava/lang/Comparable;");
            jobject upper=wdCallObjectMethod(env,aeRange,"getUpper","()Ljava/lang/Comparable;");
            char temp[0x100]={0};

            snprintf(temp,0x100,"%dx%d",wdCallIntMethod(env,lower,"intValue","()I"),
                     wdCallIntMethod(env,upper,"intValue","()I"));
            cJSON_AddStringToObject(item_json_item,"aeRange",temp);
            logd(WD_COLLECT,"aeRange= %s",temp);
            (*env)->DeleteLocalRef(env, lower);
            (*env)->DeleteLocalRef(env, upper);

        }
        //获取maxAe
        jobject maxAe=wdCallObjectMethod(env,cameraCharacteristics,"get"
                ,"(Landroid/hardware/camera2/CameraCharacteristics$Key;)Ljava/lang/Object;",CONTROL_MAX_REGIONS_AE);
        if(maxAe!=NULL){
            int num=wdCallIntMethod(env,maxAe,"intValue","()I");
            cJSON_AddNumberToObject(item_json_item,"maxAe",num);
            logd(WD_COLLECT,"maxAe= %d",num);
        }
        //获取maxAf
        jobject maxAf=wdCallObjectMethod(env,cameraCharacteristics,"get"
                ,"(Landroid/hardware/camera2/CameraCharacteristics$Key;)Ljava/lang/Object;",CONTROL_MAX_REGIONS_AF);
        if(maxAf!=NULL){
            int num=wdCallIntMethod(env,maxAf,"intValue","()I");
            cJSON_AddNumberToObject(item_json_item,"maxAf",num);
            logd(WD_COLLECT,"maxAf= %d",num);
        }
        //获取物理尺寸
        jobject phy=wdCallObjectMethod(env,cameraCharacteristics,"get"
                ,"(Landroid/hardware/camera2/CameraCharacteristics$Key;)Ljava/lang/Object;",SENSOR_INFO_PHYSICAL_SIZE);
        if (phy != NULL) {
            jstring str_jstring = wdCallObjectMethod(env,phy,"toString","()Ljava/lang/String;");
            const char *str =(*env)->GetStringUTFChars(env,str_jstring,0);
            cJSON_AddStringToObject(item_json_item,"phy",str);
            logd(WD_COLLECT,"phy= %s",str);
            (*env)->ReleaseStringUTFChars(env,str_jstring, str);
            (*env)->DeleteLocalRef(env, str_jstring);
        }
        //获取最大的像素阵列
        jobject pixel=wdCallObjectMethod(env,cameraCharacteristics,"get"
                ,"(Landroid/hardware/camera2/CameraCharacteristics$Key;)Ljava/lang/Object;",SENSOR_INFO_PIXEL_ARRAY_SIZE);
        if (pixel != NULL) {
            jstring str_jstring =wdCallObjectMethod(env,pixel,"toString","()Ljava/lang/String;");
            const char *str =(*env)->GetStringUTFChars(env,str_jstring,0);
            cJSON_AddStringToObject(item_json_item,"pixel",str);
            logd(WD_COLLECT,"pixel= %s",str);
            (*env)->ReleaseStringUTFChars(env,str_jstring, str);
            (*env)->DeleteLocalRef(env, str_jstring);
        }
        cJSON_AddItemToArray(item_json, item_json_item);
        (*env)->ReleaseStringUTFChars(env,cameraId, camera_str);
        (*env)->DeleteLocalRef(env, cameraId);
        (*env)->DeleteLocalRef(env, cameraCharacteristics);
        (*env)->DeleteLocalRef(env, maxZoom);
        (*env)->DeleteLocalRef(env, pixel);
        (*env)->DeleteLocalRef(env, phy);
        (*env)->DeleteLocalRef(env, maxAf);
        (*env)->DeleteLocalRef(env, maxAe);
        (*env)->DeleteLocalRef(env, aeRange);
        (*env)->DeleteLocalRef(env, ori);
        (*env)->DeleteLocalRef(env, maxPixel);
    }
    (*env)->DeleteLocalRef(env, camera_Str);
    (*env)->DeleteLocalRef(env, cameraManager_obj);
    (*env)->DeleteLocalRef(env, cameraIdList_Obj);

    cJSON_AddItemToObject(json, "camera", item_json);

    logd(WD_COLLECT, "%s", "collect camera info finished...");
}

void collect_battery_info(JNIEnv *env, cJSON *json) {
    cJSON* item_json = cJSON_CreateObject();

    //get battery capacity
    jobject power_profile = wdNewObject(env, "com/android/internal/os/PowerProfile", "(Landroid/content/Context;)V", g_app_context);
    jstring ca_jstr = (*env)->NewStringUTF(env, "battery.capacity");
    jdouble capacity = wdCallDoubleMethod(env, power_profile, "getAveragePower", "(Ljava/lang/String;)D", ca_jstr);
    cJSON_AddNumberToObject(item_json, "capacity", capacity);
    (*env)->DeleteLocalRef(env, power_profile);
    (*env)->DeleteLocalRef(env, ca_jstr);

    jobject action_jstr = (*env)->NewStringUTF(env, "android.intent.action.BATTERY_CHANGED");
    jobject intent_filter = wdNewObject(env, "android/content/IntentFilter", "(Ljava/lang/String;)V", action_jstr);
    jobject intent = wdCallObjectMethod(env, g_app_context, "registerReceiver",
            "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;", NULL, intent_filter);
    const char* key[] = {"status", "plugged", "level", "scale", "voltage", "temperature"};
    for(int i = 0; i < sizeof(key)/sizeof(const char*); ++i) {
        jstring key_str = (*env)->NewStringUTF(env, key[i]);
        jint value = wdCallIntMethod(env, intent, "getIntExtra", "(Ljava/lang/String;I)I", key_str, -1);
        cJSON_AddNumberToObject(item_json, key[i], value);
        (*env)->DeleteLocalRef(env, key_str);
    }

    cJSON_AddItemToObject(json, "battery", item_json);
    (*env)->DeleteLocalRef(env, action_jstr);
    (*env)->DeleteLocalRef(env, intent_filter);
    (*env)->DeleteLocalRef(env, intent);

    logd(WD_COLLECT, "%s", "collect battery_info finished...");
}

void collect_env(JNIEnv *env, cJSON *json) {
    cJSON* env_item = cJSON_CreateObject();
    char* path = getenv("PATH");
    cJSON_AddStringToObject(env_item, "PATH", path);
    cJSON_AddItemToObject(json, "env", env_item);

    logd(WD_COLLECT, "%s", "collect env finished...");
}

void collect_libs_info(JNIEnv *env, cJSON *json) {
    cJSON* item = cJSON_CreateObject();
    int pid = WDSYSCALL(SYS_getpid);
    logd(WD_COLLECT, "my pid: %d", pid);
    const char* lib_names[] = {"libwdun.so", "libc.so"};
    for(int j = 0; j < sizeof(lib_names)/sizeof(const char*); ++j) {
        char cmd[64] = {0};
        char line[1024] = {0};
        snprintf(cmd, 63, "cat /proc/%d/maps | grep %s", pid, lib_names[j]);
        FILE *fp = g_funcs.wd_popen(cmd, "r");
        if(fp){
            if(fgets(line, 1024, fp)) {
                int pos = 0;
                sscanf(line, "%*lx-%*lx %*4s %*lx %*lx:%*lx %*d%n", &pos);
                char* path = wd_util_trim(line + pos);
                int fd = WDSYSCALL(SYS_openat, AT_FDCWD, path, O_RDONLY, 0);
                if(fd < 0) break;
                int size = lseek(fd, 0, SEEK_END);
                //映射内存
                int mmap_call_num = 222;
                uint8_t* base = (uint8_t*) WDSYSCALL(WD_SYS_mmap, 0, size, PROT_READ, MAP_SHARED, fd, 0);
                uint8_t out[16] = {0};
                char md5_str[32 + 1] = {0};
                MD5(base, size, out);
                bytes2Hex(out, md5_str, 16, 0);
                cJSON_AddStringToObject(item, lib_names[j], md5_str);
                //取消内存映射
                WDSYSCALL(SYS_munmap, base, size);
                //关闭文件
                WDSYSCALL(SYS_close, fd);
            }
            g_funcs.wd_pclose(fp);
        }
    }
    cJSON_AddItemToObject(json, "libs", item);
    logd(WD_COLLECT, "%s", "collect libs_md5 finished...");
}

void collect_cpu_info(JNIEnv *env, cJSON *json) {
    cJSON* item = cJSON_CreateObject();
    //cpu count
    char count[10] = {0}, minfreq[20] = {0}, maxfreq[20] = {0}, features[100] = {0};
    int res = read_file("/sys/devices/system/cpu/present", "r", count, 9);
    cJSON_AddStringToObject(item, "count", count);

    res = read_file("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq", "r", minfreq, 19);
    cJSON_AddStringToObject(item, "minfreq", minfreq);

    res = read_file("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", "r", maxfreq, 19);
    cJSON_AddStringToObject(item, "maxfreq", maxfreq);

    res = read_cmd("cat /proc/cpuinfo | grep Features", "r", features, 99);
    char* ft = strchr(features, ' ') + 1;
    cJSON_AddStringToObject(item, "features", ft);

    cJSON_AddItemToObject(json, "cpu", item);

    logd(WD_COLLECT, "%s", "collect cpu info finished...");

}

void collect_sensor_info(JNIEnv *env, cJSON *json) {
    cJSON *item = cJSON_CreateArray();
    jstring sm_str = (*env)->NewStringUTF(env, "sensor");
    jobject sm = wdCallObjectMethod(env, g_app_context, "getSystemService",
                                    "(Ljava/lang/String;)Ljava/lang/Object;", sm_str);
    jobject sensor_list = wdCallObjectMethod(env, sm, "getSensorList", "(I)Ljava/util/List;", -1);
    jint size = wdCallIntMethod(env, sensor_list, "size", "()I");
    for (int i = 0; i < size; ++i) {
        jobject sensor = wdCallObjectMethod(env, sensor_list, "get", "(I)Ljava/lang/Object;", i);
        jint type = wdCallIntMethod(env, sensor, "getType", "()I");
        jobject name_jstr = wdCallObjectMethod(env, sensor, "getName", "()Ljava/lang/String;");
        jobject vendor_jstr = wdCallObjectMethod(env, sensor, "getVendor", "()Ljava/lang/String;");
        const char *name = (*env)->GetStringUTFChars(env, name_jstr, NULL);
        const char *vendor = (*env)->GetStringUTFChars(env, vendor_jstr, NULL);
        char buf[256] = {0};
        snprintf(buf, 255, "%d, %s, %s", type, name, vendor);
        cJSON_AddItemToArray(item, cJSON_CreateString(buf));
        (*env)->ReleaseStringUTFChars(env, name_jstr, name);
        (*env)->ReleaseStringUTFChars(env, vendor_jstr, vendor);
        (*env)->DeleteLocalRef(env, sensor);
    }
    (*env)->DeleteLocalRef(env, sensor_list);
    (*env)->DeleteLocalRef(env, sm);
    (*env)->DeleteLocalRef(env, sm_str);
    cJSON_AddItemToObject(json, "sensor", item);
    logd(WD_COLLECT, "%s", "collect sensor_info finished...");
}

void collect_mem_info(JNIEnv *env, cJSON *json) {
    cJSON* item = cJSON_CreateObject();

    //get ram
    struct sysinfo si;
    if(WDSYSCALL(SYS_sysinfo, &si) == 0) {
        u_long ram_total = si.totalram * si.mem_unit;
        u_long ram_free = si.freeram * si.mem_unit;
        char ram_buf[32] =  {0};
        snprintf(ram_buf, 31, "%lu, %lu", ram_total, ram_free);
        cJSON_AddStringToObject(item, "ram", ram_buf);
    }

    //get rom
    struct statfs sf;
    if(WDSYSCALL(SYS_statfs, "/data", &sf) == 0) {
        u_long data_total = sf.f_blocks * sf.f_bsize;
        u_long data_free = sf.f_bfree * sf.f_bsize;
        char sdcard_buf[32] =  {0};
        snprintf(sdcard_buf, 31, "%lu, %lu", data_total, data_free);
        cJSON_AddStringToObject(item, "rom", sdcard_buf);
    }

    //get sdcard
    /*
    struct statfs sf2;
    if(WDSYSCALL(SYS_statfs, "/sdcard", &sf2) == 0) {
        u_long sdcard_total = sf2.f_blocks * sf2.f_bsize;
        u_long sdcard_free = sf2.f_bfree * sf2.f_bsize;
        char sdcard_buf[32] =  {0};
        snprintf(sdcard_buf, 31, "%lu, %lu", sdcard_total, sdcard_free);
        cJSON_AddStringToObject(item, "sdcard", sdcard_buf);
    }*/

    cJSON_AddItemToObject(json, "mem", item);
    logd(WD_COLLECT, "%s", "collect mem_info finished...");
}

void collect_network_info(JNIEnv *env, cJSON *json) {
    cJSON* network = cJSON_CreateObject();
    jstring ci_jstr = (*env)->NewStringUTF(env, "connectivity");
    jobject ci = wdCallObjectMethod(env, g_app_context, "getSystemService",
                                    "(Ljava/lang/String;)Ljava/lang/Object;", ci_jstr);
    jobject ni = wdCallObjectMethod(env, ci, "getActiveNetworkInfo", "()Landroid/net/NetworkInfo;");
    if (ni == NULL) goto return_label;
    jint conn_type = wdCallIntMethod(env, ni, "getType", "()I");

    //mobile
    if (conn_type == 0) {
        char *network_type = "";
        jint sub_type = wdCallIntMethod(env, ni, "getSubtype", "()I");
        switch (sub_type) {
            case 1 :
            case 2 :
            case 4 :
            case 7 :
            case 11 :
            case 16 :
                network_type = "2g";
                break;
            case 3 :
            case 5 :
            case 6 :
            case 8 :
            case 9 :
            case 10 :
            case 12 :
            case 14 :
            case 15 :
            case 17 :
                network_type = "3g";
                break;
            case 13 :
            case 18 :
            case 19 :
                network_type = "4g";
                break;
            case 20 :
                network_type = "5g";
                break;
            default:
                break;
        }
        cJSON_AddStringToObject(network, "network_type", network_type);
    }

    //wifi
    else if (conn_type == 1) {
        cJSON_AddStringToObject(network, "network_type", "wifi");
        //get wifi ssid bssid ip, need ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION permission on Android high version
        jstring wm_jstr = (*env)->NewStringUTF(env, "wifi");
        jobject wm = wdCallObjectMethod(env, g_app_context, "getSystemService",
                                        "(Ljava/lang/String;)Ljava/lang/Object;", wm_jstr);
        jobject wi = wdCallObjectMethod(env, wm, "getConnectionInfo", "()Landroid/net/wifi/WifiInfo;");
        jobject ssid = wdCallObjectMethod(env, wi, "getSSID", "()Ljava/lang/String;");
        addJniStringToJson(env, network, "ssid", ssid);
        (*env)->DeleteLocalRef(env, ssid);
        jobject bssid = wdCallObjectMethod(env, wi, "getBSSID", "()Ljava/lang/String;");
        addJniStringToJson(env, network, "bssid", bssid);
        (*env)->DeleteLocalRef(env, bssid);
        jint wifi_ip_int = wdCallIntMethod(env, wi, "getIpAddress", "()I");
        char wifi_ip[32] = {0};
        inet_ntop(AF_INET, &wifi_ip_int, wifi_ip, 31);
        cJSON_AddStringToObject(network, "wifi_ip", wifi_ip);
        (*env)->DeleteLocalRef(env, wi);
        (*env)->DeleteLocalRef(env, wm);
        (*env)->DeleteLocalRef(env, wm_jstr);
    }

    //dns
    cJSON* dns = cJSON_CreateArray();
    jobject nw = wdCallObjectMethod(env, ci, "getActiveNetwork", "()Landroid/net/Network;");
    if(nw != NULL) {
        jobject lp = wdCallObjectMethod(env, ci, "getLinkProperties", "(Landroid/net/Network;)Landroid/net/LinkProperties;", nw);
        if(lp != NULL) {
            jobject dns_list = wdCallObjectMethod(env, lp, "getDnsServers", "()Ljava/util/List;");
            if(dns_list != NULL) {
                jint size = wdCallIntMethod(env, dns_list, "size", "()I");
                for(int i = 0; i < size; ++i) {
                    jobject ia = wdCallObjectMethod(env, dns_list, "get", "(I)Ljava/lang/Object;", i);
                    jstring address_jstr = wdCallObjectMethod(env, ia, "getHostAddress", "()Ljava/lang/String;");
                    addJniStringToArray(env, dns, address_jstr);
                    (*env)->DeleteLocalRef(env, address_jstr);
                }
                (*env)->DeleteLocalRef(env, dns_list);
            }
            (*env)->DeleteLocalRef(env, lp);
        }
        (*env)->DeleteLocalRef(env, nw);
    }


    cJSON_AddItemToObject(network, "dns", dns);

    (*env)->DeleteLocalRef(env, ni);
    return_label:
    (*env)->DeleteLocalRef(env, ci);
    (*env)->DeleteLocalRef(env, ci_jstr);

    cJSON_AddItemToObject(json, "network", network);

    logd(WD_COLLECT, "%s", "collect network info finished...");
}

void collect_user_agent(JNIEnv *env, cJSON *json) {
    jobject ua_jstr = wdCallStaticObjectMethod(env, "android/webkit/WebSettings", "getDefaultUserAgent",
            "(Landroid/content/Context;)Ljava/lang/String;", g_app_context);
    addJniStringToJson(env, json, "user-agent", ua_jstr);
    (*env)->DeleteLocalRef(env, ua_jstr);

    jstring key_jstr = (*env)->NewStringUTF(env, "http.agent");
    jobject ua2_jstr = wdCallStaticObjectMethod(env, "java/lang/System", "getProperty",
                                              "(Ljava/lang/String;)Ljava/lang/String;", key_jstr);
    addJniStringToJson(env, json, "user-agent2", ua2_jstr);

    (*env)->DeleteLocalRef(env, key_jstr);
    (*env)->DeleteLocalRef(env, ua2_jstr);

    logd(WD_COLLECT, "%s", "collect user agent finished...");
}

void collect_location_info(JNIEnv *env, cJSON *json) {
    //get location info need ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION permission.
    cJSON* item = cJSON_CreateObject();

    jstring loc_jstr = (*env)->NewStringUTF(env, "location");
    jobject lm = wdCallObjectMethod(env, g_app_context, "getSystemService",
                                    "(Ljava/lang/String;)Ljava/lang/Object;", loc_jstr);
    jobject providers = wdCallObjectMethod(env, lm, "getAllProviders", "()Ljava/util/List;");
    if(providers == NULL) goto return_label;
    jint size = wdCallIntMethod(env, providers, "size", "()I");
    int get_loc = 0;
    for(int i = 0; i < size; ++i) {
        jobject provider_jstr = wdCallObjectMethod(env, providers, "get", "(I)Ljava/lang/Object;", i);
        //check if location provider is available
        if(wdCallBooleanMethod(env, lm, "isProviderEnabled", "(Ljava/lang/String;)Z", provider_jstr) == JNI_TRUE) {
            jobject loc = wdCallObjectMethod(env, lm, "getLastKnownLocation",
                                             "(Ljava/lang/String;)Landroid/location/Location;", provider_jstr);
            if(loc != NULL) {
                jdouble lat = wdCallDoubleMethod(env, loc, "getLatitude", "()D");
                jdouble lon = wdCallDoubleMethod(env, loc, "getLongitude", "()D");
                cJSON_AddNumberToObject(item, "lat", lat);
                cJSON_AddNumberToObject(item, "lon", lon);
                get_loc = 1;
            }
            (*env)->DeleteLocalRef(env, loc);
        }
        (*env)->DeleteLocalRef(env, provider_jstr);
        if(get_loc) break;
    }
    (*env)->DeleteLocalRef(env, providers);
    return_label:
    (*env)->DeleteLocalRef(env, lm);
    (*env)->DeleteLocalRef(env, loc_jstr);
    cJSON_AddItemToObject(json, "location", item);
    logd(WD_COLLECT, "%s", "collect location info finished...");
}

void collect_cell_info(JNIEnv *env, cJSON *json) {
    //cell info need ACCESSS_COARSE_LOCATION permission
    cJSON* item = cJSON_CreateObject();

    jstring phone_jstr = (*env)->NewStringUTF(env, "phone");
    jobject tm = wdCallObjectMethod(env, g_app_context, "getSystemService",
                                    "(Ljava/lang/String;)Ljava/lang/Object;", phone_jstr);
    jobject cl = wdCallObjectMethod(env, tm, "getCellLocation", "()Landroid/telephony/CellLocation;");
    if(cl == NULL) goto return_label;
    jclass gsm_cls = (*env)->FindClass(env, "android/telephony/gsm/GsmCellLocation");
    if((*env)->IsInstanceOf(env, cl, gsm_cls) == JNI_TRUE) {
        jint cid = wdCallIntMethod(env, cl, "getCid", "()I");
        jint lac = wdCallIntMethod(env, cl, "getLac", "()I");
        jint psc = wdCallIntMethod(env, cl, "getPsc", "()I");
        cJSON_AddNumberToObject(item, "cid", cid);
        cJSON_AddNumberToObject(item, "lac", lac);
        cJSON_AddNumberToObject(item, "psc", psc);
    }
    (*env)->DeleteLocalRef(env, gsm_cls);
    return_label:
    (*env)->DeleteLocalRef(env, cl);
    (*env)->DeleteLocalRef(env, tm);
    (*env)->DeleteLocalRef(env, phone_jstr);

    cJSON_AddItemToObject(json, "cell", item);
    logd(WD_COLLECT, "%s", "collect cell info finished...");
}

void collect_system_id(JNIEnv *env, cJSON *json) {
    char buf1[40] = {0}, buf2[40] = {0};
    int res = read_file("/proc/sys/kernel/random/boot_id", "r", buf1, 39);
    cJSON_AddStringToObject(json, "sys_boot_id", buf1);

    res = read_file("/proc/sys/kernel/random/uuid", "r", buf2, 39);
    cJSON_AddStringToObject(json, "sys_uuid", buf2);

    logd(WD_COLLECT, "%s", "collect system id finished...");
}

void collect_time_info(JNIEnv *env, cJSON *json) {
    // current_time
    struct timespec ts = {0};
    time_t cur;
    if(WDSYSCALL(SYS_clock_gettime, CLOCK_REALTIME, &ts) == 0) {
        cur = ts.tv_sec;
        cJSON_AddNumberToObject(json, "cur_time", cur);
    }

    if(WDSYSCALL(SYS_clock_gettime, CLOCK_BOOTTIME, &ts) == 0) {
        cJSON_AddNumberToObject(json, "boot_time", cur - ts.tv_sec);
    }

    logd(WD_COLLECT, "%s", "collect time info finished...");
}

// 收集风控信息
void collect_risk_info(JNIEnv *env, cJSON *json) {
    cJSON * risk_item = cJSON_CreateObject();

    //root
    //su
    char su[64] = {0};
    int res = read_cmd("which su", "r", su, 63);
    cJSON_AddStringToObject(risk_item, "su", su);

    //magisk
    char magisk[64] = {0};
    res = read_cmd("which magisk", "r", magisk, 63);
    cJSON_AddStringToObject(risk_item, "magisk", magisk);

    //ifconfig
    cJSON* ifconfig = cJSON_CreateArray();
    int sock = WDSYSCALL(SYS_socket, AF_INET, SOCK_DGRAM, 0);
    struct ifconf ifc = {0, 0};
    if(WDSYSCALL(SYS_ioctl, sock, SIOCGIFCONF, &ifc) == 0) {
        struct ifreq *ifr = malloc(ifc.ifc_len);
        ifc.ifc_ifcu.ifcu_req = ifr;
        if (WDSYSCALL(SYS_ioctl, sock, SIOCGIFCONF, &ifc) == 0) {
            int if_num = ifc.ifc_len / sizeof(struct ifreq);
            for(int i = 0; i < if_num; ++i) {
                cJSON_AddItemToArray(ifconfig, cJSON_CreateString(ifr[i].ifr_ifrn.ifrn_name));
            }
        }
        free(ifr);
    }
    cJSON_AddItemToObject(risk_item, "ifconfig", ifconfig);

    //emulator
    const char *emulator_files[] = {
            "/dev/socket/qemud",
            "/dev/qemu_pipe",
            "/goldfish",
            "/system/lib/libc_malloc_debug_qemu.so",
            "/sys/qemu_trace",
            "/system/bin/microvirt-prop",
            "/system/lib/libdroid4x.so",
            "/system/bin/windroyed",
            "/system/bin/microvirtd",
            "/system/bin/nox-prop",
            "/system/bin/ttVM-prop",
            "/system/bin/droid4x-prop",
            "/data/.bluestacks.prop",
            "/data/data/com.bluestacks.setup",
            "/dev/vboxuser",
            "/fstab.vbox86",
            "/init.vbox86.rc",
            "/init.svc.vbox86-setup",
            "/sys/bus/pci/drivers/vboxguest/module",
            "/sys/bus/pci/drivers/vboxguest/new_id",
            "/sys/bus/pci/drivers/vboxguest/remove_id",
            "/sys/bus/pci/drivers/vboxguest/uevent",
            "/sys/bus/platform/drivers/qemu_pipe",
            "/sys/bus/platform/drivers/qemu_trace",
            "/init.svc.droid4x",
            "/init.svc.noxd",
            "/init.svc.microvirtd",
            "/ro.kernel.android.qemud",
            "/androVM.vbox_dpi",
            "/androVM.vbox_graph_mode",
            "/system/bin/qemu_props",
            "/system/bin/androVM-prop",
            "/system/lib/libaligl2.so",
            "/system/lib/libalitag.so",
            "/system/lib/libcloudletevent.so",
            "/system/lib/libcloudletos.so",
            "/system/lib/nemuguest.ko",
            "/system/lib/nemusf.ko",
            "/system/lib/nemuvideo.ko",
            "/system/lib/vpipe.ko",
            "/system/lib/vpipe_novt.ko",
            "/system/lib/vboxguest.ko",
            "/system/lib/vboxsf.ko",
            //    "/system/lib/libhoudini.so",             //x86转arm架构
            "/sys/bus/virtio",
            "/sys/module/virtio_net",
            "/sys/module/virtio_pci",
            "/sys/class/virtio_pt/virtiopt",
            "/sys/devices/virtual/virtio_pt/virtiopt",
            "/sys/class/virtio_pt",
            "/dev/virtiopt",
            "/sys/bus/pci/drivers/virtio-pci",
            "/proc/sys/fs/binfmt_misc/arm"
    };

    cJSON *emulator = cJSON_CreateArray();
    for (int i = 0; i < sizeof(emulator_files) / sizeof(const char *); ++i) {
        if (is_file_exists(emulator_files[i])) {
            cJSON_AddItemToArray(emulator, cJSON_CreateString(emulator_files[i]));
        }
    }
    cJSON_AddItemToObject(risk_item, "emulator", emulator);

    //hook

    //inject
    cJSON *inject = cJSON_CreateArray();
    const char* xposed_strs[]  = {
            "libriru.so", "libriru_edxp.so", "libsandhook.edxp.so", "liblspd.so", "XposedBridge.jar",
            "frida-agent-32.so", "frida-agent-64.so", "libsubstrate.so", "libxposed_art.so"
    };
    int pid = WDSYSCALL(SYS_getpid);
    for(int i = 0; i < sizeof(xposed_strs)/sizeof(const char*); ++i) {
        char cmd[64] = {0};
        snprintf(cmd, 63, "cat /proc/%d/maps | grep %s", pid, xposed_strs[i]);
        FILE* fp = popen(cmd, "r");
        char line[1024] = {0};
        if(fgets(line, 1023, fp)){
            int pos = 0;
            sscanf(line, "%*lx-%*lx %*4s %*lx %*lx:%*lx %*d%n", &pos);
            char* path = wd_util_trim(line + pos);
            if(path) {
                cJSON_AddItemToArray(inject, cJSON_CreateString(path));
            }
        }
        pclose(fp);
    }
    cJSON_AddItemToObject(risk_item, "inject", inject);

    //system_lib_funcs
    cJSON* sys_funcs = cJSON_CreateObject();
    struct so_info* si = fake_dlopen("libc.so", 0);
    if(si) {
        const char* symbols[] = {
                "syscall", "open", "read", "fopen", "fread", "popen", "__system_property_get", "__system_property_set"
        };
        for(int i = 0; i < sizeof(symbols)/sizeof(const char*); ++i) {
            void* addr = fake_dlsym(si, symbols[i]);
            char data[32 + 1] = {0};
            if(addr) {
                char val[16] = {0};
                memcpy(val, addr, 16);
                bytes2Hex(val, data, 16, 0);
            }
            cJSON_AddStringToObject(sys_funcs, symbols[i], data);
        }
        fake_dlclose(si);
    }
    cJSON_AddItemToObject(risk_item, "sys_funcs", sys_funcs);


    //sdk_funcs
    cJSON* sdk_funcs = cJSON_CreateObject();
    //todo
    cJSON_AddItemToObject(risk_item, "sdk_funcs", sdk_funcs);

    //multirun
    cJSON* multirun = cJSON_CreateObject();
    jobject files_dir = wdCallObjectMethod(env, g_app_context, "getFilesDir", "()Ljava/io/File;");
    jstring files_path_jstr = wdCallObjectMethod(env, files_dir, "getCanonicalPath", "()Ljava/lang/String;");
    addJniStringToJson(env, multirun, "files_path", files_path_jstr);
    cJSON_AddItemToObject(risk_item, "multirun", multirun);
    (*env)->DeleteLocalRef(env, files_path_jstr);
    (*env)->DeleteLocalRef(env, files_dir);

    cJSON_AddItemToObject(json, "risk", risk_item);
}

void collect_common_app(JNIEnv *env, cJSON *json) {
    cJSON* item = cJSON_CreateArray();
    const char* apps[] = {
            "com.tencent.mm",
            "com.tencent.mobileqq",
            "com.sina.weibo",
            "com.immomo.momo",
            "com.immomo.momo",
            "cn.kuwo.player",
            "com.tencent.qqmusic",
            "com.netease.cloudmusic",
            "com.sohu.inputmethod.sogou",
            "com.baidu.input",
            "com.iflytek.inputmethod",
            "com.tencent.qqpinyin",
            "com.eg.android.AlipayGphone",
            "com.taobao.taobao",
            "com.xunmeng.pinduoduo",
            "com.jingdong.app.mall",
            "com.baidu.searchbox",
            "com.tencent.mtt",
            "com.ss.android.article.news",
            "com.tencent.news"
    };
    for(int i = 0; i < sizeof(apps)/sizeof(const char*); ++i) {
        char path[128] = {0};
        snprintf(path, 127, "/data/data/%s", apps[i]);
        if(is_file_exists(path)) {
            cJSON_AddItemToArray(item, cJSON_CreateString(apps[i]));
        }
    }
    cJSON_AddItemToObject(json, "common_used_apps", item);
}

void collect_installed_apps(JNIEnv *env, cJSON *json) {
    cJSON *item = cJSON_CreateArray();
    FILE *fp = popen("pm list packages", "r");
    if (fp) {
        char line[128] = {0};
        while (fgets(line, 127, fp)) {
            char *package = strchr(line, ':') + 1;
            cJSON_AddItemToArray(item, cJSON_CreateString(package));
        }
        pclose(fp);
    }
    cJSON_AddItemToObject(json, "installed_apps", item);
}