package com.ym.modulecommon.utils;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;

import com.ym.modulecommon.module.PackageInfoEntity;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 手机属性上报工具类
 */
public class PhoneAttributeUtils {

    /**
     * 获取手机安装应用列表包名
     * @param context
     * @return
     */
    public static List<PackageInfoEntity> getPackageNameList(Context context) {
        List<PackageInfo> packageInfo = context.getPackageManager().getInstalledPackages(0);
        List<PackageInfoEntity> list = new ArrayList<>();
        for (PackageInfo pInfo : packageInfo) {
            list.add(new PackageInfoEntity(pInfo.packageName, pInfo.firstInstallTime+"", pInfo.lastUpdateTime+""));
        }
        return list;
    }

    /**
     * 判断手机是否root
     * 是否存在su命令，并且有执行权限
     * @return 存在su命令，并且有执行权限返回true
     */
    public static boolean isRoot() {
        File file = null;
        String[] paths = {"/system/bin/", "/system/xbin/", "/system/sbin/", "/sbin/", "/vendor/bin/", "/su/bin/"};
        try {
            for (String path : paths) {
                file = new File(path + "su");
                if (file.exists() && file.canExecute()) {
//                    Log.i(TAG, "find su in : " + path);
                    return true;
                }
            }
        } catch (Exception x) {
            x.printStackTrace();
        }
        return false;
    }

    //====================== hook ======================
    /**
     * hook检测
     * @param context
     * @return
     */
    public static boolean isHook(Context context) {
        if (findHookAppName(context) || findHookAppFile() || findHookStack()) {
            return true;
        }
        return false;
    }

    /**
     * 查找设备安装目录中是否存在hook工具
     * @param context
     * @return
     */
    private static boolean findHookAppName(Context context) {
        List<ApplicationInfo> applicationInfoList = context.getPackageManager().getInstalledApplications(PackageManager.GET_META_DATA);
        for (ApplicationInfo applicationInfo : applicationInfoList) {
            if (applicationInfo.packageName.equals("de.robv.android.xposed.installer")) {
                return true;
            }
            if (applicationInfo.packageName.equals("com.saurik.substrate")) {
                return true;
            }
        }
        return false;
    }

    /**
     * 查找设备存储中是否存在hook安装文件
     * @return
     */
    private static boolean findHookAppFile() {
        try {
            Set<String> libraries = new HashSet<String>();
            String mapsFilename = "/proc/" + android.os.Process.myPid() + "/maps";
            BufferedReader reader = new BufferedReader(new FileReader(mapsFilename));
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.endsWith(".so") || line.endsWith(".jar")) {
                    int n = line.lastIndexOf(" ");
                    libraries.add(line.substring(n + 1));
                }
            }
            reader.close();
            for (String library : libraries) {
                if (library.contains("com.saurik.substrate")) {
                    return true;
                }
                if (library.contains("XposedBridge.jar")) {
                    return true;
                }
            }
        } catch (Exception e) {}
        return false;
    }

    /**
     * 查找程序运行的栈中是否存在hook相关类
     * @return
     */
    private static boolean findHookStack() {
        try {
            throw new Exception("findhook");
        } catch (Exception e) {
            int zygoteInitCallCount = 0;
            for (StackTraceElement stackTraceElement : e.getStackTrace()) {
                if (stackTraceElement.getClassName().equals("com.android.internal.os.ZygoteInit")) {
                    zygoteInitCallCount++;
                    if (zygoteInitCallCount == 2) {
                        return true;
                    }
                }
                if (stackTraceElement.getClassName().equals("com.saurik.substrate.MS$2") && stackTraceElement.getMethodName().equals("invoked")) {
                    return true;
                }
                if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") && stackTraceElement.getMethodName().equals("main")) {
                    return true;
                }
                if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") && stackTraceElement.getMethodName().equals("handleHookedMethod")) {
                    return true;
                }
            }
        }
        return false;
    }
    //====================== hook ======================
}
