package com.ttstd.dialer.utils; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.ActivityTaskManager; import android.app.role.RoleManager; import android.bluetooth.BluetoothAdapter; import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.graphics.Bitmap; import android.graphics.Rect; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Build; import android.os.Environment; import android.os.IBinder; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.MediaStore; import android.provider.Settings; import android.telephony.CellInfo; import android.telephony.CellSignalStrength; import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.view.SurfaceControl; import android.view.WindowManager; import java.io.BufferedReader; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.NetworkInterface; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.security.MessageDigest; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; import androidx.annotation.RequiresApi; public class SystemUtils { private static final String TAG = "SystemUtils"; /** * 获取系统配置信息 * * @param key * @param defaultValue * @return */ public static String getProperty(String key, String defaultValue) { // SystemProperties.get(); try { Class c = Class.forName("android.os.SystemProperties"); Method get = c.getMethod("get", String.class, String.class); return (String) (get.invoke(c, key, defaultValue)); } catch (Exception e) { e.printStackTrace(); return defaultValue; } } /** * 获取设备序列号 * * @return */ @SuppressLint("MissingPermission") public static String getSerial() { String serial = Build.UNKNOWN; try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {//9.0+ serial = Build.getSerial(); } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {//8.0+ serial = Build.SERIAL; } else {//8.0- // Class c = Class.forName("android.os.SystemProperties"); // Method get = c.getMethod("get", String.class); // serial = (String) get.invoke(c, "ro.serialno"); getProperty("ro.serialno", serial); } } catch (Exception e) { e.printStackTrace(); Logger.e("e", "读取设备序列号异常:" + e.toString()); } return serial; } /** * 获取卡槽 0 (主卡) 的 IMEI * * @param context 上下文 * @return IMEI 字符串,如果获取失败则返回空字符串 */ @SuppressLint("MissingPermission") // 系统应用已通过 manifest 赋予权限,此处屏蔽 Lint 警告 public static String getDefaultImei(Context context) { return getImei(context, 0); } /** * 根据卡槽 ID 获取对应的 IMEI * 该方法适用于 Android 6.0 (API 23) 及以上的所有版本。 * 在 Android 10+ 上,只有系统应用能成功返回数据。 * * @param context 上下文 * @param slotIndex 卡槽索引 (0 代表卡槽1, 1 代表卡槽2) * @return IMEI 字符串,如果获取失败则返回空字符串 */ @SuppressLint("MissingPermission") public static String getImei(Context context, int slotIndex) { String imei = ""; try { TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); if (tm != null) { // 优先使用标准的 getImei(int slotIndex) 方法 (API 23+) // 对于低版本系统,可以使用反射调用,但现代系统推荐直接调用 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { imei = tm.getImei(slotIndex); } else { // 针对极少数的 Android 6.0 以下的老旧系统兜底 imei = tm.getDeviceId(slotIndex); } } } catch (Exception e) { e.printStackTrace(); } return imei == null ? "" : imei; } /** * 获取所有卡槽的 IMEI 数组 * * @param context 上下文 * @return 包含各个卡槽 IMEI 的字符串数组 */ @SuppressLint("MissingPermission") public static String[] getAllImeis(Context context) { String[] imeis = new String[2]; // 假设最多两个卡槽 imeis[0] = getImei(context, 0); imeis[1] = getImei(context, 1); return imeis; } public static String getDefaultImSi(Context context) { return getImsiBySubId(context, 0); } public static String getImsiBySubId(Context context, int count) { SubscriptionManager subscriptionManager = SubscriptionManager.from(context); SubscriptionInfo subInfo = subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(count); if (subInfo != null) { TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); int subId = subInfo.getSubscriptionId(); TelephonyManager tm = telephonyManager.createForSubscriptionId(subId); //关键点,拿到对应subId的tm String mImsi = tm.getSubscriberId(subId); //imsi?not subid? Log.e(TAG, "getSubInfo: mImsi = " + mImsi); return mImsi; } else { return Build.UNKNOWN; } } /** * 获取MAC地址 * * @param context * @return */ public static String getWlanMacAddress(Context context) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { return getMacDefault(context); } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { return getMacAddressM(); } else { return getMacFromHardware(); } } /** * Android 6.0 之前(不包括6.0) * * @param context * @return */ private static String getMacDefault(Context context) { String mac = "未获取到设备Mac地址"; if (context == null) { return mac; } WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); WifiInfo info = null; try { info = wifi.getConnectionInfo(); } catch (Exception e) { e.printStackTrace(); } if (info == null) { return mac; } mac = info.getMacAddress(); if (!TextUtils.isEmpty(mac)) { mac = mac.toUpperCase(Locale.ENGLISH); return mac; } else { return "02:00:00:00:00:00"; } } /** * Android 6.0(包括) - Android 7.0(不包括) * * @return */ private static String getMacAddressM() { String mac = "未获取到设备Mac地址"; try { mac = new BufferedReader(new FileReader("/sys/class/net/wlan0/address")).readLine(); } catch (IOException e) { e.printStackTrace(); } return mac; } /** * 遍历循环所有的网络接口,找到接口是 wlan0 * * @return */ private static String getMacFromHardware() { try { List all = Collections.list(NetworkInterface.getNetworkInterfaces()); for (NetworkInterface nif : all) { if (!nif.getName().equalsIgnoreCase("wlan0")) { continue; } byte[] macBytes = nif.getHardwareAddress(); StringBuilder res1 = new StringBuilder(); for (byte b : macBytes) { res1.append(String.format("%02X:", b)); } if (res1 != null) { res1.deleteCharAt(res1.length() - 1); } return res1.toString(); } } catch (Exception e) { e.printStackTrace(); } return "未获取到设备Mac地址"; } /** * 获取当前移动网络(数据网络)的 IP 地址 * * @return 形如 "10.x.x.x" 的 IPv4 地址,未连接数据网络或获取失败返回空串 */ @SuppressLint("MissingPermission") public static String getMobileIp(Context context) { try { // 优先通过 TrafficStats / 网络接口遍历移动网络接口(rmnet0 / ccmni0 等) List interfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); for (NetworkInterface nif : interfaces) { String name = nif.getName(); if (name == null) { continue; } // 移动数据网络接口通常以 rmnet / ccmni / mnet 开头 if (name.startsWith("rmnet") || name.startsWith("ccmni") || name.startsWith("mnet")) { for (java.net.InterfaceAddress addr : nif.getInterfaceAddresses()) { java.net.InetAddress inet = addr.getAddress(); if (inet instanceof java.net.Inet4Address && !inet.isLoopbackAddress()) { return inet.getHostAddress(); } } } } } catch (Exception e) { e.printStackTrace(); } return ""; } /** * 获取移动数据网络的 MAC 地址 * 注意:Android 6.0+ 出于隐私限制,通过 API 只能拿到 02:00:00:00:00:00, * 系统签名应用可尝试读取 /sys/class/net/rmnet0/address(多数情况下为空)。 * * @return MAC 地址字符串,无法获取时返回空串 */ @SuppressLint("MissingPermission") public static String getMobileMac() { // 移动网络接口通常没有独立 MAC,部分设备通过 rmnet 暴露 String[] candidates = {"rmnet0", "rmnet_data0", "ccmni0"}; for (String ifName : candidates) { try { NetworkInterface nif = NetworkInterface.getByName(ifName); if (nif != null) { byte[] mac = nif.getHardwareAddress(); if (mac != null && mac.length > 0) { StringBuilder sb = new StringBuilder(); for (byte b : mac) { sb.append(String.format("%02X:", b)); } if (sb.length() > 0) { sb.deleteCharAt(sb.length() - 1); } return sb.toString(); } } } catch (Exception e) { e.printStackTrace(); } } return ""; } /** * 获取移动网络子类型名称(如 LTE / HSPA / EDGE 等) * * @param tm TelephonyManager * @return 网络子类型描述,未接入数据网络返回空串 */ @SuppressLint("MissingPermission") public static String getNetworkSubtype(TelephonyManager tm) { try { if (tm != null) { int subtype = tm.getNetworkType(); return getNetworkTypeName(subtype); } } catch (Exception e) { e.printStackTrace(); } return ""; } /** * 获取当前蜂窝信号强度等级(0~4,对应 ASU 的粗略分级,4 最强)。 * 该值可用于状态栏信号格显示,与 {@link android.telephony.SignalStrength#getLevel()} 一致。 * * @param context 上下文 * @return 信号等级 0~4;无 SIM / 非蜂窝网络 / 获取失败返回 -1 */ @SuppressLint("MissingPermission") public static int getSignalLevel(Context context) { try { TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); if (tm == null) { return -1; } // Android 9 (API 28) 及以下无 getAllCellInfo/SignalStrength 的公开 getLevel, // 这里统一用 getAllCellInfo 取最强小区的 level,覆盖绝大多数系统版本。 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { List cellInfos = tm.getAllCellInfo(); if (cellInfos != null) { int maxLevel = -1; for (CellInfo info : cellInfos) { if (info == null || !info.isRegistered()) { continue; } CellSignalStrength strength = info.getCellSignalStrength(); if (strength != null) { maxLevel = Math.max(maxLevel, strength.getLevel()); } } if (maxLevel >= 0) { return maxLevel; } } } // 兜底:API 29+ 可直接取 SignalStrength 的 level if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { SignalStrength signalStrength = tm.getSignalStrength(); if (signalStrength != null) { return signalStrength.getLevel(); } } } catch (Exception e) { e.printStackTrace(); } return -1; } /** * 获取当前蜂窝信号强度(dBm,如 -75 表示较强,-113 接近无信号)。 * 优先使用各制式 CellInfo 的 dBm,找不到则回退到 ASU 换算(asu - 113 - 2 * (asu & 1))。 * * @param context 上下文 * @return 信号强度 dBm;无信号 / 获取失败返回 {@code null} */ @SuppressLint("MissingPermission") public static Integer getSignalDbm(Context context) { try { TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); if (tm == null) { return null; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { List cellInfos = tm.getAllCellInfo(); if (cellInfos != null) { int bestDbm = Integer.MIN_VALUE; for (CellInfo info : cellInfos) { if (info == null || !info.isRegistered()) { continue; } CellSignalStrength strength = info.getCellSignalStrength(); if (strength != null) { int dbm = strength.getDbm(); if (dbm != Integer.MAX_VALUE && dbm > bestDbm) { bestDbm = dbm; } } } if (bestDbm != Integer.MIN_VALUE) { return bestDbm; } } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { SignalStrength signalStrength = tm.getSignalStrength(); if (signalStrength != null) { int dbm = signalStrength.getDbm(); if (dbm != Integer.MAX_VALUE && dbm != Integer.MIN_VALUE) { return dbm; } } } } catch (Exception e) { e.printStackTrace(); } return null; } /** * 根据当前活跃网络获取信号强度 (RSSI/dBm) * 优先返回 Wi-Fi 信号强度,如果未连接 Wi-Fi 则返回移动网络信号强度 * * @param context 上下文 * @return 信号强度 (dBm);获取失败返回 null */ public static Integer getCurrentNetworkRssi(Context context) { try { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); if (cm == null) return null; NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); if (activeNetwork == null || !activeNetwork.isConnected()) { return null; } if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) { WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); WifiInfo info = wifiManager.getConnectionInfo(); if (info != null) { return info.getRssi(); } } else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) { return getSignalDbm(context); } } catch (Exception e) { e.printStackTrace(); } return null; } private static String getNetworkTypeName(int networkType) { switch (networkType) { case TelephonyManager.NETWORK_TYPE_GPRS: return "GPRS"; case TelephonyManager.NETWORK_TYPE_EDGE: return "EDGE"; case TelephonyManager.NETWORK_TYPE_UMTS: return "UMTS"; case TelephonyManager.NETWORK_TYPE_HSDPA: return "HSDPA"; case TelephonyManager.NETWORK_TYPE_HSUPA: return "HSUPA"; case TelephonyManager.NETWORK_TYPE_HSPA: return "HSPA"; case TelephonyManager.NETWORK_TYPE_CDMA: return "CDMA"; case TelephonyManager.NETWORK_TYPE_EVDO_0: return "EVDO_0"; case TelephonyManager.NETWORK_TYPE_EVDO_A: return "EVDO_A"; case TelephonyManager.NETWORK_TYPE_1xRTT: return "1xRTT"; case TelephonyManager.NETWORK_TYPE_EHRPD: return "eHRPD"; case TelephonyManager.NETWORK_TYPE_LTE: return "LTE"; case TelephonyManager.NETWORK_TYPE_HSPAP: return "HSPA+"; case TelephonyManager.NETWORK_TYPE_GSM: return "GSM"; case TelephonyManager.NETWORK_TYPE_TD_SCDMA: return "TD_SCDMA"; case TelephonyManager.NETWORK_TYPE_IWLAN: return "IWLAN"; case TelephonyManager.NETWORK_TYPE_NR: return "NR"; default: return "UNKNOWN"; } } /** * 计算已安装应用包名列表的 MD5(与 SnSecurityInfoReq.mobileAppList 口径保持一致: * 取所有非系统应用包名,按字母排序后用逗号拼接再取 MD5)。 * * @param pm PackageManager * @return 32 位小写 MD5 字符串;无应用或异常时返回空串 */ public static String getAppListMd5(PackageManager pm) { try { if (pm == null) { return ""; } List installedApps = pm.getInstalledPackages(0); List pkgNames = new ArrayList<>(); for (PackageInfo info : installedApps) { // 仅统计第三方(非系统)应用,与常规应用列表统计口径一致 if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 && info.packageName != null) { pkgNames.add(info.packageName); } } Collections.sort(pkgNames); StringBuilder sb = new StringBuilder(); for (String name : pkgNames) { if (sb.length() > 0) { sb.append(","); } sb.append(name); } return md5(sb.toString()); } catch (Exception e) { e.printStackTrace(); } return ""; } private static String md5(String input) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] digest = md.digest(input.getBytes()); StringBuilder sb = new StringBuilder(); for (byte b : digest) { sb.append(String.format("%02x", b & 0xff)); } return sb.toString(); } catch (Exception e) { e.printStackTrace(); } return ""; } public static String getFactoryMacAddresses(Context context) { WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); String[] factoryMacAddresses = wifiManager.getFactoryMacAddresses(); String str = (factoryMacAddresses == null || factoryMacAddresses.length <= 0) ? Build.UNKNOWN : factoryMacAddresses[0]; return str.toUpperCase(Locale.ENGLISH); } public static String getBluetoothMacAddress(Context context) { String mac = Settings.Secure.getString( context.getContentResolver(), "bluetooth_address" ); return mac; } @Deprecated public static String getBtAddressByReflection() { String def = "02:00:00:00:00:00"; BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter == null) { return def; } return bluetoothAdapter.getAddress(); // Field field; // try { // field = BluetoothAdapter.class.getDeclaredField("mService"); // field.setAccessible(true); // Object bluetoothManagerService = field.get(bluetoothAdapter); // if (bluetoothManagerService == null) { // return def; // } // Method method = bluetoothManagerService.getClass().getMethod("getAddress"); // if (method != null) { // Object obj = method.invoke(bluetoothManagerService); // if (obj != null) { // return obj.toString(); // } // } // } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { // e.printStackTrace(); // } // return def; } public static String getBuildId() { return getProperty("ro.build.id", Build.ID); } public static String getBuildDisplayId() { return getProperty("ro.build.display.id", Build.DISPLAY); } // ================================================================== // 设备基本信息(对齐后端 DeviceSystemInfoVO) // ================================================================== public static String getDeviceName(Context context) { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { return Settings.Global.getString(context.getContentResolver(), Settings.Global.DEVICE_NAME); } } catch (Throwable t) { // ignore } return Build.UNKNOWN; } public static String getDeviceManufacturer() { return getProperty("ro.product.manufacturer", Build.MANUFACTURER); } public static String getHardware() { return getProperty("ro.hardware", Build.UNKNOWN); } public static String getHost() { return getProperty("ro.build.host", Build.HOST); } public static String getAndroidId(Context context) { try { return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); } catch (Throwable t) { return ""; } } public static String getBuildFingerprint() { return getProperty("ro.build.fingerprint", Build.FINGERPRINT); } public static String getBuildType() { return getProperty("ro.build.type", Build.TYPE); } public static String getBuildUser() { return getProperty("ro.build.user", Build.USER); } public static String getBuildHost() { return getProperty("ro.build.host", Build.HOST); } public static String getBuildTags() { return getProperty("ro.build.tags", Build.TAGS); } public static long getBuildTime() { try { return Build.TIME; } catch (Throwable t) { return 0L; } } public static String getLanguage() { try { return Locale.getDefault().getLanguage(); } catch (Throwable t) { return ""; } } public static String getTimezone() { try { return java.util.TimeZone.getDefault().getID(); } catch (Throwable t) { return ""; } } public static long getBootTime() { try { return System.currentTimeMillis() - android.os.SystemClock.elapsedRealtime(); } catch (Throwable t) { return 0L; } } public static String getScreenResolution(Context context) { try { android.util.DisplayMetrics dm = getRealDisplayMetrics(context); return dm.widthPixels + "x" + dm.heightPixels; } catch (Throwable t) { return ""; } } /** * 获取设备真实屏幕显示参数(包含状态栏、导航栏,不被窗口裁切)。 * 优先使用 getRealMetrics(显示设备真实分辨率),低版本无此 API 时回退 getMetrics。 */ private static android.util.DisplayMetrics getRealDisplayMetrics(Context context) { android.util.DisplayMetrics dm = new android.util.DisplayMetrics(); android.view.WindowManager wm = (android.view.WindowManager) context.getSystemService(Context.WINDOW_SERVICE); android.view.Display display = wm.getDefaultDisplay(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { display.getRealMetrics(dm); } else { display.getMetrics(dm); } return dm; } public static int getScreenDensity(Context context) { try { return (int) Math.round(context.getResources().getDisplayMetrics().density); } catch (Throwable t) { return 0; } } public static int getScreenDensityDpi(Context context) { try { return context.getResources().getDisplayMetrics().densityDpi; } catch (Throwable t) { return 0; } } public static String getKernelVersion() { return getProperty("sys.kernel.version", ""); } public static String getRomVersion() { return getProperty("ro.build.version.emui", getProperty("ro.build.version.miui", getProperty("ro.build.version.opporom", getProperty("ro.vivo.os.version", "")))); } public static String getProductName() { return getProperty("ro.product.name", Build.PRODUCT); } public static String getProductModel() { return getProperty("ro.product.model", Build.UNKNOWN); } public static String getProductDevice() { return getProperty("ro.product.device", Build.UNKNOWN); } public static String getBoardPlatform() { return getProperty("ro.board.platform", Build.UNKNOWN); } public static String getDeviceType(Context context) { try { int layout = context.getResources().getConfiguration().screenLayout & android.content.res.Configuration.SCREENLAYOUT_SIZE_MASK; if (layout >= android.content.res.Configuration.SCREENLAYOUT_SIZE_LARGE) { return "tablet"; } return "phone"; } catch (Throwable t) { return "phone"; } } public static int getIsTablet(Context context) { try { int layout = context.getResources().getConfiguration().screenLayout & android.content.res.Configuration.SCREENLAYOUT_SIZE_MASK; return layout >= android.content.res.Configuration.SCREENLAYOUT_SIZE_LARGE ? 1 : 0; } catch (Throwable t) { return 0; } } public static String getReleaseVersion() { return getProperty("ro.build.version.release", Build.VERSION.RELEASE); } public static String getDisplayVersion() { return getProperty("ro.build.display.id", Build.DISPLAY); } public static String getBootloader() { return getProperty("ro.bootloader", Build.BOOTLOADER); } public static String getBasebandVersion() { return getProperty("gsm.version.baseband", Build.UNKNOWN); } public static String getRadioVersion() { try { return Build.getRadioVersion() != null ? Build.getRadioVersion() : Build.UNKNOWN; } catch (Throwable t) { return Build.UNKNOWN; } } public static String getScreenSize(Context context) { try { android.util.DisplayMetrics dm = getRealDisplayMetrics(context); double diagonal = Math.sqrt(Math.pow(dm.widthPixels / dm.xdpi, 2) + Math.pow(dm.heightPixels / dm.ydpi, 2)); return String.format(Locale.US, "%.1f", diagonal); } catch (Throwable t) { return ""; } } public static double getScreenInch(Context context) { try { android.util.DisplayMetrics dm = getRealDisplayMetrics(context); double diagonal = Math.sqrt(Math.pow(dm.widthPixels / dm.xdpi, 2) + Math.pow(dm.heightPixels / dm.ydpi, 2)); return Math.round(diagonal * 10.0) / 10.0; } catch (Throwable t) { return 0.0; } } public static int getRefreshRate(Context context) { try { android.view.Display display = ((android.view.WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); return Math.round(display.getRefreshRate()); } catch (Throwable t) { return 0; } } public static String getOsType() { return "Android"; } public static String getOsVersion() { return Build.VERSION.RELEASE; } public static String getFirmwareVersion() { return getProperty("ro.build.version.incremental", Build.UNKNOWN); } public static boolean isMainProcessName(Context cxt, int pid) { String packageName = cxt.getPackageName(); ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE); List runningApps = am.getRunningAppProcesses(); if (runningApps == null) { return false; } for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) { if (procInfo.pid == pid) { return procInfo.processName.equals(packageName); } } return false; } public static String getForegroundActivityPackageName(Context context) { ActivityManager activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); List runningTaskInfos = activityManager.getRunningTasks(1); if (runningTaskInfos != null && !runningTaskInfos.isEmpty()) { ComponentName componentName = runningTaskInfos.get(0).topActivity; if (componentName != null) { String currentPackageName = componentName.getPackageName(); return currentPackageName; } } return ""; } /** * 获取栈顶的应用包名 */ public static String getForegroundActivityClassName(Context context) { ActivityManager manager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); String currentClassName = manager.getRunningTasks(1).get(0).topActivity.getClassName(); return currentClassName; } public static List getRunningTaskPackages(Context context) { ActivityManager activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); List runningTaskInfos = activityManager.getRunningTasks(Integer.MAX_VALUE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return runningTaskInfos.stream().map(new Function() { @Override public String apply(ActivityManager.RunningTaskInfo runningTaskInfo) { return runningTaskInfo.topActivity.getPackageName(); } }).collect(Collectors.toList()); } else { List packageNames = new ArrayList<>(); for (ActivityManager.RunningTaskInfo runningTaskInfo : runningTaskInfos) { packageNames.add(runningTaskInfo.topActivity.getPackageName()); } return packageNames; } } public static void killBackgroundProcesses(Context context, String processName) { Logger.e(TAG, "killBackgroundProcesses: " + processName); ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); String packageName; try { if (!processName.contains(":")) { packageName = processName; } else { packageName = processName.split(":")[0]; } activityManager.killBackgroundProcesses(packageName); activityManager.forceStopPackage(packageName); // removeTask(context, processName); // Method forceStopPackage = activityManager.getClass() // .getDeclaredMethod("forceStopPackage", String.class); // forceStopPackage.setAccessible(true); // forceStopPackage.invoke(activityManager, packageName); } catch (Exception e) { e.printStackTrace(); } } public static void removeTask(Context context, String packageName) { Logger.e(TAG, "removeTask: " + packageName); // if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { List list = getRecentTasks(ActivityManager.getMaxRecentTasksStatic(), getCurrentUserId()); HashMap taskMap = new HashMap<>(); for (ActivityManager.RecentTaskInfo info : list) { taskMap.put(info.realActivity.getPackageName(), info.id); } try { ActivityManagerNative.getDefault().removeTask(taskMap.get(packageName)); } catch (RemoteException e) { e.printStackTrace(); Logger.e(TAG, "removeTask: " + e.getMessage()); } catch (NullPointerException e) { Logger.e(TAG, "removeTask: " + e.getMessage()); } // } else { // ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); // // } } /** * @return a list of the recents tasks. * 获取近期任务列表 */ public static List getRecentTasks(int numTasks, int userId) { try { return ActivityTaskManager.getService().getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId).getList(); } catch (RemoteException e) { Logger.e(TAG, "Failed to get recent tasks " + e); return new ArrayList<>(); } } /** * @return the current user's id. * 获取userId */ public static int getCurrentUserId() { UserInfo ui; try { ui = ActivityManager.getService().getCurrentUser(); return ui != null ? ui.id : 0; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } public static boolean isDefaultLauncher(Context context, Class launcherActivityClass) { ComponentName componentName = new ComponentName(context, launcherActivityClass); Logger.e(TAG, "isDefaultLauncher: componentName = " + componentName); Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 0); if (resolveInfo != null && resolveInfo.activityInfo != null) { ComponentName defaultComponentName = resolveInfo.getComponentInfo().getComponentName(); Logger.e(TAG, "isDefaultLauncher: defaultComponentName = " + defaultComponentName); return componentName.equals(defaultComponentName); } return false; } /** * 将指定的 Activity 设置为系统默认桌面(需要系统签名) * * @param context 上下文 * @param launcherActivityClass 你的桌面 Activity 类,例如 MyLauncherActivity.class */ public static void setDefaultLauncher(Context context, Class launcherActivityClass) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { addRoleHolderAsUser(context, context.getPackageName()); } else { PackageManager pm = context.getPackageManager(); // 1. 创建桌面过滤的 IntentFilter IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN); filter.addCategory(Intent.CATEGORY_HOME); filter.addCategory(Intent.CATEGORY_DEFAULT); // 2. 查询当前系统中所有的桌面应用(用来构建 ComponentName 数组) Intent homeIntent = new Intent(Intent.ACTION_MAIN); homeIntent.addCategory(Intent.CATEGORY_HOME); List resolveInfos = pm.queryIntentActivities(homeIntent, PackageManager.MATCH_DEFAULT_ONLY); int bestMatch = 0; ComponentName[] set = new ComponentName[resolveInfos.size()]; for (int i = 0; i < resolveInfos.size(); i++) { ResolveInfo info = resolveInfos.get(i); set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name); if (info.match > bestMatch) { bestMatch = info.match; // 获取最匹配的值 } } // 3. 构建你自己的桌面 ComponentName ComponentName myLauncher = new ComponentName(context, launcherActivityClass); // 4. 替换首选 Activity(核心步骤) try { // 注意:API 29 (Android 10) 中此方法对第三方应用废弃,但对系统签名应用依然有效 pm.replacePreferredActivity(filter, bestMatch, set, myLauncher); Logger.i("LauncherHelper", "成功设置为默认桌面"); } catch (SecurityException e) { Logger.e("LauncherHelper", "缺少权限或未生效,请检查系统签名是否正确", e); } catch (Exception e) { Logger.e("LauncherHelper", "设置默认桌面失败", e); } } } public static void setDefaultLauncher(Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { RoleManager roleManager = (RoleManager) context.getSystemService(Context.ROLE_SERVICE); // 检查当前应用是否已经是桌面角色持有者 if (roleManager != null && !roleManager.isRoleHeld(RoleManager.ROLE_HOME)) { // 检查该角色是否可用 if (roleManager.isRoleAvailable(RoleManager.ROLE_HOME)) { // 创建请求意图 Intent roleRequestIntent = roleManager.createRequestRoleIntent(RoleManager.ROLE_HOME); // 注意:在普通应用中,这会弹出系统选择框 // 在系统签名应用中,这通常能直接提升优先级或简化流程 context.startActivity(roleRequestIntent); } } } else { // Android 10 以下的传统做法:清除当前默认并弹出选择 Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } } /** * 将指定包名设置为默认桌面 (需要系统签名) */ public static void addRoleHolderAsUser(Context context, String packageName) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { Logger.e(TAG, "RoleManager requires Android 10 or higher."); return; } RoleManager roleManager = context.getSystemService(RoleManager.class); if (roleManager == null) return; String roleName = RoleManager.ROLE_HOME; // 1. 检查是否已经是当前角色持有者,避免重复调用 if (roleManager.isRoleHeld(roleName)) { // 注意:这里最好再判断一下持有的包名是否为目标包名 Logger.i(TAG, "Role " + roleName + " is already held by some package."); // 如果已经是自己,直接返回 } try { UserHandle user = Process.myUserHandle(); Executor executor = context.getMainExecutor(); // 定义回调 Consumer callback = successful -> { if (successful) { Logger.i(TAG, "Successfully set " + packageName + " as " + roleName); } else { Logger.e(TAG, "Failed to set " + packageName + " as " + roleName + ". Check system logs."); } }; // 2. 使用反射调用隐藏方法 addRoleHolderAsUser // 方法签名: addRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer) Method method = RoleManager.class.getMethod("addRoleHolderAsUser", String.class, String.class, int.class, UserHandle.class, Executor.class, Consumer.class); Logger.d(TAG, "Invoking addRoleHolderAsUser for package: " + packageName); method.invoke(roleManager, roleName, packageName, 0, user, executor, callback); // roleManager.addRoleHolderAsUser(roleName, packageName, 0, user, executor, callback); } catch (NoSuchMethodException e) { Logger.e(TAG, "Method not found. Is this a non-standard ROM?", e); } catch (Exception e) { Logger.e(TAG, "Error invoking addRoleHolderAsUser", e); } } public static void setOtherDefaultLauncher(Context context) { PackageManager pm = context.getPackageManager(); Intent homeIntent = new Intent(Intent.ACTION_MAIN); homeIntent.addCategory(Intent.CATEGORY_HOME); List resolveInfos = pm.queryIntentActivities(homeIntent, PackageManager.MATCH_DEFAULT_ONLY); Optional systemLauncher = resolveInfos.stream() .filter(info -> !context.getPackageName().equals(info.activityInfo.packageName)) .filter(info -> ApkUtils.isSystemApp(context, info.activityInfo.packageName)) .findFirst(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { systemLauncher.ifPresent(info -> addRoleHolderAsUser(context, info.activityInfo.packageName)); } else { pm.clearPackagePreferredActivities(context.getPackageName()); systemLauncher.ifPresent(info -> { IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN); filter.addCategory(Intent.CATEGORY_HOME); filter.addCategory(Intent.CATEGORY_DEFAULT); int bestMatch = 0; ComponentName[] set = new ComponentName[resolveInfos.size()]; for (int i = 0; i < resolveInfos.size(); i++) { ResolveInfo res = resolveInfos.get(i); set[i] = new ComponentName(res.activityInfo.packageName, res.activityInfo.name); if (res.match > bestMatch) bestMatch = res.match; } ComponentName otherLauncher = new ComponentName(info.activityInfo.packageName, info.activityInfo.name); try { pm.replacePreferredActivity(filter, bestMatch, set, otherLauncher); } catch (Exception e) { Logger.e(TAG, "setOtherDefaultLauncher error", e); } }); } } /** * 系统签名应用专用:静默获取全屏截图Bitmap * * @param context 上下文 * @return 全屏截图Bitmap,失败返回null */ public static Bitmap takeFullScreenshot(Context context) { // 获取屏幕真实宽高(包含状态栏、导航栏) WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics metrics = new DisplayMetrics(); wm.getDefaultDisplay().getRealMetrics(metrics); int screenWidth = metrics.widthPixels; int screenHeight = metrics.heightPixels; Logger.e(TAG, "takeFullScreenshot: screenWidth " + screenWidth); Logger.e(TAG, "takeFullScreenshot: screenHeight " + screenHeight); try { // 注意:不同 Android 版本的 SurfaceControl.screenshot 方法签名可能不同 // 以下代码适用于 Android 9.0 及以上版本常见的隐藏 API 调用方式 // 1. 获取屏幕显示的 IBinder (通常为 DisplayControl 或 SurfaceControl 的内部方法) // 在较高版本中,可能需要通过 SurfaceControl.getInternalDisplayToken() 获取 // 2. 反射调用 screenshot 方法 Class surfaceControlClass = Class.forName("android.view.SurfaceControl"); // Android 10+ 建议使用新的反射路径,这里以通用逻辑为例: // 对于 Android 11+,Google 引入了 SurfaceControl.LayerCaptureArgs 等内部类 // 这是一个针对 Android 9/10 的简化逻辑参考: Bitmap bitmap = (Bitmap) surfaceControlClass.getDeclaredMethod("screenshot", Rect.class, Integer.TYPE, Integer.TYPE, Integer.TYPE) .invoke(null, new Rect(), screenWidth, screenHeight, 0); return bitmap; } catch (Exception e) { e.printStackTrace(); return null; } } @RequiresApi(api = Build.VERSION_CODES.Q) public static Bitmap takeScreenshotHighVersion() { try { // 1. 获取主屏幕的 Token (Internal Display) // 反射调用 SurfaceControl.getInternalDisplayToken() Method getInternalDisplayTokenMethod = SurfaceControl.class.getDeclaredMethod("getInternalDisplayToken"); getInternalDisplayTokenMethod.setAccessible(true); IBinder displayToken = (IBinder) getInternalDisplayTokenMethod.invoke(null); if (displayToken == null) return null; // 2. 构造 DisplayCaptureArgs.Builder (Android 11+ 的新包装类) Class builderClass = Class.forName("android.view.SurfaceControl$DisplayCaptureArgs$Builder"); Constructor builderConstructor = builderClass.getConstructor(IBinder.class); Object builder = builderConstructor.newInstance(displayToken); // 可以通过 Builder 设置缩放、格式等,这里直接 build() Method buildMethod = builderClass.getDeclaredMethod("build"); Object captureArgs = buildMethod.invoke(builder); // 3. 调用 SurfaceControl.screenshot(DisplayCaptureArgs) // 返回值是一个 ScreenshotHardwareBuffer 对象 Class captureArgsClass = Class.forName("android.view.SurfaceControl$DisplayCaptureArgs"); Method screenshotMethod = SurfaceControl.class.getDeclaredMethod("screenshot", captureArgsClass); screenshotMethod.setAccessible(true); Object screenshotBuffer = screenshotMethod.invoke(null, captureArgs); if (screenshotBuffer == null) return null; // 4. 从 ScreenshotHardwareBuffer 中提取 Bitmap Method asBitmapMethod = screenshotBuffer.getClass().getDeclaredMethod("asBitmap"); asBitmapMethod.setAccessible(true); return (Bitmap) asBitmapMethod.invoke(screenshotBuffer); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 保存Bitmap到公共图库(Android 10+ 无需存储权限) * * @param context 上下文 * @param bitmap 截图Bitmap * @return 保存成功返回true */ public static boolean saveScreenshotToGallery(Context context, Bitmap bitmap) { if (bitmap == null) return false; // 使用MediaStore保存到Pictures目录(Android 10+ 推荐) ContentValues values = new android.content.ContentValues(); values.put(MediaStore.Images.Media.MIME_TYPE, "image/png"); values.put(MediaStore.Images.Media.DISPLAY_NAME, "Screenshot_" + System.currentTimeMillis() + ".png"); values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/SystemScreenshots"); android.net.Uri uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); if (uri == null) return false; try (OutputStream outputStream = context.getContentResolver().openOutputStream(uri)) { bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream); return true; } catch (java.io.IOException e) { e.printStackTrace(); return false; } } /** * 智能截屏:根据系统版本选择最佳方案 * 1. 优先使用 SurfaceControl 反射 (速度快,且避开 Android 10+ 的 "Bad system call") * 2. 失败则回退到 screencap 命令行 * * @param context 上下文 * @param filepath 保存路径 * @param observer 结果回调 (0 为成功) */ public static void screenshotSnap(Context context, String filepath, Observer observer) { Observable.fromCallable(new Callable() { @Override public Integer call() throws Exception { Bitmap bitmap = null; // 1. 对于 Android 9.0 及以上,优先尝试反射 SurfaceControl try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { bitmap = takeScreenshotHighVersion(); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { bitmap = takeFullScreenshot(context); } if (bitmap != null) { try (FileOutputStream fos = new FileOutputStream(filepath)) { boolean success = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); if (success) { Logger.i(TAG, "screenshotSnap: SurfaceControl 截屏成功"); return 0; } } } } catch (Exception e) { Logger.e(TAG, "SurfaceControl 截屏异常: " + e.getMessage()); } // 2. Fallback: 使用命令行 (适用于旧版本或已 Root 设备) Logger.w(TAG, "screenshotSnap: 尝试使用命令行回退"); CmdUtil.Result result = CmdUtil.execute("screencap -p " + filepath); if (result.code != 0) { Logger.e(TAG, "screenshotSnap 命令行失败: " + result.error); } return result.code; } }).subscribe(observer); } public static void screenshotCmd(String filepath, Observer observer) { Observable.fromCallable(new Callable() { @Override public Integer call() throws Exception { CmdUtil.Result result = CmdUtil.execute("screencap -p " + filepath); Logger.e(TAG, "call: " + result.error); return result.code; } }).subscribe(observer); } /** * 检查指定的 VoiceInteractionService 是否被设置为系统默认助手 */ public static boolean isAssistantServiceEnabled(Context context, String serviceName) { String setting = Settings.Secure.getString(context.getContentResolver(), "assistant"); if (setting != null) { ComponentName componentName = ComponentName.unflattenFromString(setting); if (componentName != null) { return componentName.getPackageName().equals(context.getPackageName()) && componentName.getClassName().equals(serviceName); } } return false; } /** * 尝试将当前应用的指定服务设置为系统默认助手(通常需要系统签名或 WRITE_SECURE_SETTINGS 权限) */ public static boolean setAssistantService(Context context, String serviceName) { try { String value = context.getPackageName() + "/" + serviceName; boolean success1 = Settings.Secure.putString(context.getContentResolver(), "assistant", value); boolean success2 = Settings.Secure.putString(context.getContentResolver(), "voice_interaction_service", value); return success1 && success2; } catch (Exception e) { Logger.e(TAG, "setAssistantService error: " + e.getMessage()); return false; } } /** * 查询设备当前屏幕是否亮屏。 */ public static boolean isScreenOn(Context context) { try { PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); return powerManager != null && powerManager.isInteractive(); } catch (Throwable e) { return false; } } }