refactor(push): 重构设备信息上报为五类并发上传
将原来的单一硬件信息上报拆分为系统、硬件、网络、安全、其他五类信息,通过 mergeDelayError 并发上传,避免单点失败阻塞,并新增 DeviceInfoCollector 统一采集逻辑。
This commit is contained in:
@@ -4,138 +4,290 @@ import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 设备硬件信息上传请求。
|
||||
* <p>
|
||||
* 对应后端 {@code POST /api/v1/sn/update_hardware_info},
|
||||
* 字段与后端 {@code SnHardwareInfoReq} 一一对应。
|
||||
* <p>
|
||||
* 使用包装类型(Integer)而非基本类型,未采集到的字段保持 null,
|
||||
* Gson 默认不序列化 null,可避免用 0 覆盖服务端已有数据。
|
||||
*/
|
||||
public class SnHardwareInfoReq implements Serializable {
|
||||
private static final long serialVersionUID = 2728925136862797858L;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SerializedName("sn_imei")
|
||||
private String imei;
|
||||
@SerializedName("cpu_abi")
|
||||
private String cpuAbi;
|
||||
|
||||
@SerializedName("sn_imsi")
|
||||
private String imsi;
|
||||
@SerializedName("cpu_abi2")
|
||||
private String cpuAbi2;
|
||||
|
||||
@SerializedName("sn_wlan_mac")
|
||||
private String wlanMac;
|
||||
@SerializedName("cpu_min")
|
||||
private String cpuMin;
|
||||
|
||||
@SerializedName("sn_device_mac")
|
||||
private String deviceMac;
|
||||
@SerializedName("cpu_model")
|
||||
private String cpuModel;
|
||||
|
||||
@SerializedName("sn_bluetooth_mac")
|
||||
private String bluetoothMac;
|
||||
@SerializedName("cpu_cores")
|
||||
private Integer cpuCores;
|
||||
|
||||
@SerializedName("sn_model")
|
||||
private String model;
|
||||
@SerializedName("cpu_max_freq")
|
||||
private String cpuMaxFreq;
|
||||
|
||||
@SerializedName("sn_brand")
|
||||
private String brand;
|
||||
@SerializedName("cpu_min_freq")
|
||||
private String cpuMinFreq;
|
||||
|
||||
@SerializedName("sn_board")
|
||||
private String board;
|
||||
@SerializedName("total_ram")
|
||||
private String totalRam;
|
||||
|
||||
@SerializedName("sn_android_version")
|
||||
private String androidVersion;
|
||||
@SerializedName("available_ram")
|
||||
private String availableRam;
|
||||
|
||||
@SerializedName("sn_android_api")
|
||||
private int androidApi;
|
||||
@SerializedName("internal_storage")
|
||||
private String internalStorage;
|
||||
|
||||
@SerializedName("sn_build_id")
|
||||
private String buildId;
|
||||
@SerializedName("available_storage")
|
||||
private String availableStorage;
|
||||
|
||||
@SerializedName("sn_build_display_id")
|
||||
private String buildDisplayId;
|
||||
@SerializedName("external_storage")
|
||||
private String externalStorage;
|
||||
|
||||
public String getImei() {
|
||||
return imei;
|
||||
@SerializedName("battery_capacity")
|
||||
private String batteryCapacity;
|
||||
|
||||
@SerializedName("battery_level")
|
||||
private Integer batteryLevel;
|
||||
|
||||
@SerializedName("battery_status")
|
||||
private String batteryStatus;
|
||||
|
||||
@SerializedName("battery_health")
|
||||
private String batteryHealth;
|
||||
|
||||
@SerializedName("battery_temp")
|
||||
private String batteryTemp;
|
||||
|
||||
@SerializedName("battery_voltage")
|
||||
private String batteryVoltage;
|
||||
|
||||
@SerializedName("battery_technology")
|
||||
private String batteryTechnology;
|
||||
|
||||
@SerializedName("gpu_renderer")
|
||||
private String gpuRenderer;
|
||||
|
||||
@SerializedName("gpu_vendor")
|
||||
private String gpuVendor;
|
||||
|
||||
@SerializedName("gpu_version")
|
||||
private String gpuVersion;
|
||||
|
||||
@SerializedName("sensors")
|
||||
private String sensors;
|
||||
|
||||
@SerializedName("support_abi")
|
||||
private String supportAbi;
|
||||
|
||||
@SerializedName("support_abi64")
|
||||
private String supportAbi64;
|
||||
|
||||
public String getCpuAbi() {
|
||||
return cpuAbi;
|
||||
}
|
||||
|
||||
public void setImei(String imei) {
|
||||
this.imei = imei;
|
||||
public void setCpuAbi(String cpuAbi) {
|
||||
this.cpuAbi = cpuAbi;
|
||||
}
|
||||
|
||||
public String getImsi() {
|
||||
return imsi;
|
||||
public String getCpuAbi2() {
|
||||
return cpuAbi2;
|
||||
}
|
||||
|
||||
public void setImsi(String imsi) {
|
||||
this.imsi = imsi;
|
||||
public void setCpuAbi2(String cpuAbi2) {
|
||||
this.cpuAbi2 = cpuAbi2;
|
||||
}
|
||||
|
||||
public String getWlanMac() {
|
||||
return wlanMac;
|
||||
public String getCpuMin() {
|
||||
return cpuMin;
|
||||
}
|
||||
|
||||
public void setWlanMac(String wlanMac) {
|
||||
this.wlanMac = wlanMac;
|
||||
public void setCpuMin(String cpuMin) {
|
||||
this.cpuMin = cpuMin;
|
||||
}
|
||||
|
||||
public String getDeviceMac() {
|
||||
return deviceMac;
|
||||
public String getCpuModel() {
|
||||
return cpuModel;
|
||||
}
|
||||
|
||||
public void setDeviceMac(String deviceMac) {
|
||||
this.deviceMac = deviceMac;
|
||||
public void setCpuModel(String cpuModel) {
|
||||
this.cpuModel = cpuModel;
|
||||
}
|
||||
|
||||
public String getBluetoothMac() {
|
||||
return bluetoothMac;
|
||||
public Integer getCpuCores() {
|
||||
return cpuCores;
|
||||
}
|
||||
|
||||
public void setBluetoothMac(String bluetoothMac) {
|
||||
this.bluetoothMac = bluetoothMac;
|
||||
public void setCpuCores(Integer cpuCores) {
|
||||
this.cpuCores = cpuCores;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
public String getCpuMaxFreq() {
|
||||
return cpuMaxFreq;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
public void setCpuMaxFreq(String cpuMaxFreq) {
|
||||
this.cpuMaxFreq = cpuMaxFreq;
|
||||
}
|
||||
|
||||
public String getBrand() {
|
||||
return brand;
|
||||
public String getCpuMinFreq() {
|
||||
return cpuMinFreq;
|
||||
}
|
||||
|
||||
public void setBrand(String brand) {
|
||||
this.brand = brand;
|
||||
public void setCpuMinFreq(String cpuMinFreq) {
|
||||
this.cpuMinFreq = cpuMinFreq;
|
||||
}
|
||||
|
||||
public String getBoard() {
|
||||
return board;
|
||||
public String getTotalRam() {
|
||||
return totalRam;
|
||||
}
|
||||
|
||||
public void setBoard(String board) {
|
||||
this.board = board;
|
||||
public void setTotalRam(String totalRam) {
|
||||
this.totalRam = totalRam;
|
||||
}
|
||||
|
||||
public String getAndroidVersion() {
|
||||
return androidVersion;
|
||||
public String getAvailableRam() {
|
||||
return availableRam;
|
||||
}
|
||||
|
||||
public void setAndroidVersion(String androidVersion) {
|
||||
this.androidVersion = androidVersion;
|
||||
public void setAvailableRam(String availableRam) {
|
||||
this.availableRam = availableRam;
|
||||
}
|
||||
|
||||
public int getAndroidApi() {
|
||||
return androidApi;
|
||||
public String getInternalStorage() {
|
||||
return internalStorage;
|
||||
}
|
||||
|
||||
public void setAndroidApi(int androidApi) {
|
||||
this.androidApi = androidApi;
|
||||
public void setInternalStorage(String internalStorage) {
|
||||
this.internalStorage = internalStorage;
|
||||
}
|
||||
|
||||
public String getBuildId() {
|
||||
return buildId;
|
||||
public String getAvailableStorage() {
|
||||
return availableStorage;
|
||||
}
|
||||
|
||||
public void setBuildId(String buildId) {
|
||||
this.buildId = buildId;
|
||||
public void setAvailableStorage(String availableStorage) {
|
||||
this.availableStorage = availableStorage;
|
||||
}
|
||||
|
||||
public String getBuildDisplayId() {
|
||||
return buildDisplayId;
|
||||
public String getExternalStorage() {
|
||||
return externalStorage;
|
||||
}
|
||||
|
||||
public void setBuildDisplayId(String buildDisplayId) {
|
||||
this.buildDisplayId = buildDisplayId;
|
||||
public void setExternalStorage(String externalStorage) {
|
||||
this.externalStorage = externalStorage;
|
||||
}
|
||||
|
||||
public String getBatteryCapacity() {
|
||||
return batteryCapacity;
|
||||
}
|
||||
|
||||
public void setBatteryCapacity(String batteryCapacity) {
|
||||
this.batteryCapacity = batteryCapacity;
|
||||
}
|
||||
|
||||
public Integer getBatteryLevel() {
|
||||
return batteryLevel;
|
||||
}
|
||||
|
||||
public void setBatteryLevel(Integer batteryLevel) {
|
||||
this.batteryLevel = batteryLevel;
|
||||
}
|
||||
|
||||
public String getBatteryStatus() {
|
||||
return batteryStatus;
|
||||
}
|
||||
|
||||
public void setBatteryStatus(String batteryStatus) {
|
||||
this.batteryStatus = batteryStatus;
|
||||
}
|
||||
|
||||
public String getBatteryHealth() {
|
||||
return batteryHealth;
|
||||
}
|
||||
|
||||
public void setBatteryHealth(String batteryHealth) {
|
||||
this.batteryHealth = batteryHealth;
|
||||
}
|
||||
|
||||
public String getBatteryTemp() {
|
||||
return batteryTemp;
|
||||
}
|
||||
|
||||
public void setBatteryTemp(String batteryTemp) {
|
||||
this.batteryTemp = batteryTemp;
|
||||
}
|
||||
|
||||
public String getBatteryVoltage() {
|
||||
return batteryVoltage;
|
||||
}
|
||||
|
||||
public void setBatteryVoltage(String batteryVoltage) {
|
||||
this.batteryVoltage = batteryVoltage;
|
||||
}
|
||||
|
||||
public String getBatteryTechnology() {
|
||||
return batteryTechnology;
|
||||
}
|
||||
|
||||
public void setBatteryTechnology(String batteryTechnology) {
|
||||
this.batteryTechnology = batteryTechnology;
|
||||
}
|
||||
|
||||
public String getGpuRenderer() {
|
||||
return gpuRenderer;
|
||||
}
|
||||
|
||||
public void setGpuRenderer(String gpuRenderer) {
|
||||
this.gpuRenderer = gpuRenderer;
|
||||
}
|
||||
|
||||
public String getGpuVendor() {
|
||||
return gpuVendor;
|
||||
}
|
||||
|
||||
public void setGpuVendor(String gpuVendor) {
|
||||
this.gpuVendor = gpuVendor;
|
||||
}
|
||||
|
||||
public String getGpuVersion() {
|
||||
return gpuVersion;
|
||||
}
|
||||
|
||||
public void setGpuVersion(String gpuVersion) {
|
||||
this.gpuVersion = gpuVersion;
|
||||
}
|
||||
|
||||
public String getSensors() {
|
||||
return sensors;
|
||||
}
|
||||
|
||||
public void setSensors(String sensors) {
|
||||
this.sensors = sensors;
|
||||
}
|
||||
|
||||
public String getSupportAbi() {
|
||||
return supportAbi;
|
||||
}
|
||||
|
||||
public void setSupportAbi(String supportAbi) {
|
||||
this.supportAbi = supportAbi;
|
||||
}
|
||||
|
||||
public String getSupportAbi64() {
|
||||
return supportAbi64;
|
||||
}
|
||||
|
||||
public void setSupportAbi64(String supportAbi64) {
|
||||
this.supportAbi64 = supportAbi64;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
package com.ttstd.dialer.bean.req;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 设备网络信息上传请求。
|
||||
* <p>
|
||||
* 对应后端 {@code POST /api/v1/sn/update_network_info},
|
||||
* 字段与后端 {@code SnNetworkInfoReq} 一一对应。
|
||||
* <p>
|
||||
* 布尔语义字段(is_connected/is_vpn/is_proxy)后端为 Integer,
|
||||
* 这里同样用 Integer 表示 0/1,未知时为 null。
|
||||
*/
|
||||
public class SnNetworkInfoReq implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SerializedName("network_type")
|
||||
private String networkType;
|
||||
|
||||
@SerializedName("network_subtype")
|
||||
private String networkSubtype;
|
||||
|
||||
@SerializedName("is_connected")
|
||||
private Integer isConnected;
|
||||
|
||||
@SerializedName("wifi_ssid")
|
||||
private String wifiSsid;
|
||||
|
||||
@SerializedName("wifi_bssid")
|
||||
private String wifiBssid;
|
||||
|
||||
@SerializedName("wifi_ip")
|
||||
private String wifiIp;
|
||||
|
||||
@SerializedName("wifi_mac")
|
||||
private String wifiMac;
|
||||
|
||||
@SerializedName("wifi_gateway")
|
||||
private String wifiGateway;
|
||||
|
||||
@SerializedName("wifi_netmask")
|
||||
private String wifiNetmask;
|
||||
|
||||
@SerializedName("wifi_dns")
|
||||
private String wifiDns;
|
||||
|
||||
@SerializedName("wifi_rssi")
|
||||
private Integer wifiRssi;
|
||||
|
||||
@SerializedName("wifi_speed")
|
||||
private String wifiSpeed;
|
||||
|
||||
@SerializedName("mobile_ip")
|
||||
private String mobileIp;
|
||||
|
||||
@SerializedName("mobile_mac")
|
||||
private String mobileMac;
|
||||
|
||||
@SerializedName("mobile_imei")
|
||||
private String mobileImei;
|
||||
|
||||
@SerializedName("mobile_imsi")
|
||||
private String mobileImsi;
|
||||
|
||||
@SerializedName("mobile_operator")
|
||||
private String mobileOperator;
|
||||
|
||||
@SerializedName("mobile_sim_serial")
|
||||
private String mobileSimSerial;
|
||||
|
||||
@SerializedName("mobile_network_type")
|
||||
private String mobileNetworkType;
|
||||
|
||||
@SerializedName("bluetooth_mac")
|
||||
private String bluetoothMac;
|
||||
|
||||
@SerializedName("bluetooth_name")
|
||||
private String bluetoothName;
|
||||
|
||||
@SerializedName("is_vpn")
|
||||
private Integer isVpn;
|
||||
|
||||
@SerializedName("is_proxy")
|
||||
private Integer isProxy;
|
||||
|
||||
@SerializedName("proxy_host")
|
||||
private String proxyHost;
|
||||
|
||||
@SerializedName("proxy_port")
|
||||
private String proxyPort;
|
||||
|
||||
public String getNetworkType() {
|
||||
return networkType;
|
||||
}
|
||||
|
||||
public void setNetworkType(String networkType) {
|
||||
this.networkType = networkType;
|
||||
}
|
||||
|
||||
public String getNetworkSubtype() {
|
||||
return networkSubtype;
|
||||
}
|
||||
|
||||
public void setNetworkSubtype(String networkSubtype) {
|
||||
this.networkSubtype = networkSubtype;
|
||||
}
|
||||
|
||||
public Integer getIsConnected() {
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
public void setIsConnected(Integer isConnected) {
|
||||
this.isConnected = isConnected;
|
||||
}
|
||||
|
||||
public String getWifiSsid() {
|
||||
return wifiSsid;
|
||||
}
|
||||
|
||||
public void setWifiSsid(String wifiSsid) {
|
||||
this.wifiSsid = wifiSsid;
|
||||
}
|
||||
|
||||
public String getWifiBssid() {
|
||||
return wifiBssid;
|
||||
}
|
||||
|
||||
public void setWifiBssid(String wifiBssid) {
|
||||
this.wifiBssid = wifiBssid;
|
||||
}
|
||||
|
||||
public String getWifiIp() {
|
||||
return wifiIp;
|
||||
}
|
||||
|
||||
public void setWifiIp(String wifiIp) {
|
||||
this.wifiIp = wifiIp;
|
||||
}
|
||||
|
||||
public String getWifiMac() {
|
||||
return wifiMac;
|
||||
}
|
||||
|
||||
public void setWifiMac(String wifiMac) {
|
||||
this.wifiMac = wifiMac;
|
||||
}
|
||||
|
||||
public String getWifiGateway() {
|
||||
return wifiGateway;
|
||||
}
|
||||
|
||||
public void setWifiGateway(String wifiGateway) {
|
||||
this.wifiGateway = wifiGateway;
|
||||
}
|
||||
|
||||
public String getWifiNetmask() {
|
||||
return wifiNetmask;
|
||||
}
|
||||
|
||||
public void setWifiNetmask(String wifiNetmask) {
|
||||
this.wifiNetmask = wifiNetmask;
|
||||
}
|
||||
|
||||
public String getWifiDns() {
|
||||
return wifiDns;
|
||||
}
|
||||
|
||||
public void setWifiDns(String wifiDns) {
|
||||
this.wifiDns = wifiDns;
|
||||
}
|
||||
|
||||
public Integer getWifiRssi() {
|
||||
return wifiRssi;
|
||||
}
|
||||
|
||||
public void setWifiRssi(Integer wifiRssi) {
|
||||
this.wifiRssi = wifiRssi;
|
||||
}
|
||||
|
||||
public String getWifiSpeed() {
|
||||
return wifiSpeed;
|
||||
}
|
||||
|
||||
public void setWifiSpeed(String wifiSpeed) {
|
||||
this.wifiSpeed = wifiSpeed;
|
||||
}
|
||||
|
||||
public String getMobileIp() {
|
||||
return mobileIp;
|
||||
}
|
||||
|
||||
public void setMobileIp(String mobileIp) {
|
||||
this.mobileIp = mobileIp;
|
||||
}
|
||||
|
||||
public String getMobileMac() {
|
||||
return mobileMac;
|
||||
}
|
||||
|
||||
public void setMobileMac(String mobileMac) {
|
||||
this.mobileMac = mobileMac;
|
||||
}
|
||||
|
||||
public String getMobileImei() {
|
||||
return mobileImei;
|
||||
}
|
||||
|
||||
public void setMobileImei(String mobileImei) {
|
||||
this.mobileImei = mobileImei;
|
||||
}
|
||||
|
||||
public String getMobileImsi() {
|
||||
return mobileImsi;
|
||||
}
|
||||
|
||||
public void setMobileImsi(String mobileImsi) {
|
||||
this.mobileImsi = mobileImsi;
|
||||
}
|
||||
|
||||
public String getMobileOperator() {
|
||||
return mobileOperator;
|
||||
}
|
||||
|
||||
public void setMobileOperator(String mobileOperator) {
|
||||
this.mobileOperator = mobileOperator;
|
||||
}
|
||||
|
||||
public String getMobileSimSerial() {
|
||||
return mobileSimSerial;
|
||||
}
|
||||
|
||||
public void setMobileSimSerial(String mobileSimSerial) {
|
||||
this.mobileSimSerial = mobileSimSerial;
|
||||
}
|
||||
|
||||
public String getMobileNetworkType() {
|
||||
return mobileNetworkType;
|
||||
}
|
||||
|
||||
public void setMobileNetworkType(String mobileNetworkType) {
|
||||
this.mobileNetworkType = mobileNetworkType;
|
||||
}
|
||||
|
||||
public String getBluetoothMac() {
|
||||
return bluetoothMac;
|
||||
}
|
||||
|
||||
public void setBluetoothMac(String bluetoothMac) {
|
||||
this.bluetoothMac = bluetoothMac;
|
||||
}
|
||||
|
||||
public String getBluetoothName() {
|
||||
return bluetoothName;
|
||||
}
|
||||
|
||||
public void setBluetoothName(String bluetoothName) {
|
||||
this.bluetoothName = bluetoothName;
|
||||
}
|
||||
|
||||
public Integer getIsVpn() {
|
||||
return isVpn;
|
||||
}
|
||||
|
||||
public void setIsVpn(Integer isVpn) {
|
||||
this.isVpn = isVpn;
|
||||
}
|
||||
|
||||
public Integer getIsProxy() {
|
||||
return isProxy;
|
||||
}
|
||||
|
||||
public void setIsProxy(Integer isProxy) {
|
||||
this.isProxy = isProxy;
|
||||
}
|
||||
|
||||
public String getProxyHost() {
|
||||
return proxyHost;
|
||||
}
|
||||
|
||||
public void setProxyHost(String proxyHost) {
|
||||
this.proxyHost = proxyHost;
|
||||
}
|
||||
|
||||
public String getProxyPort() {
|
||||
return proxyPort;
|
||||
}
|
||||
|
||||
public void setProxyPort(String proxyPort) {
|
||||
this.proxyPort = proxyPort;
|
||||
}
|
||||
}
|
||||
261
app/src/main/java/com/ttstd/dialer/bean/req/SnOtherInfoReq.java
Normal file
261
app/src/main/java/com/ttstd/dialer/bean/req/SnOtherInfoReq.java
Normal file
@@ -0,0 +1,261 @@
|
||||
package com.ttstd.dialer.bean.req;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 设备其他信息上传请求。
|
||||
* <p>
|
||||
* 对应后端 {@code POST /api/v1/sn/update_other_info},
|
||||
* 字段与后端 {@code SnOtherInfoReq} 一一对应。
|
||||
* <p>
|
||||
* 注意:后端 install_time / update_time_app 为 {@code LocalDateTime},
|
||||
* Jackson 默认接受 "yyyy-MM-dd'T'HH:mm:ss" 格式的字符串,
|
||||
* 因此这里用 String 承载,由采集器负责格式化,避免时间戳数字反序列化失败。
|
||||
*/
|
||||
public class SnOtherInfoReq implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SerializedName("app_version")
|
||||
private String appVersion;
|
||||
|
||||
@SerializedName("app_version_code")
|
||||
private Integer appVersionCode;
|
||||
|
||||
@SerializedName("install_time")
|
||||
private String installTime;
|
||||
|
||||
@SerializedName("update_time_app")
|
||||
private String updateTimeApp;
|
||||
|
||||
@SerializedName("package_name")
|
||||
private String packageName;
|
||||
|
||||
@SerializedName("channel")
|
||||
private String channel;
|
||||
|
||||
@SerializedName("device_id")
|
||||
private String deviceId;
|
||||
|
||||
@SerializedName("gaid")
|
||||
private String gaid;
|
||||
|
||||
@SerializedName("oaid")
|
||||
private String oaid;
|
||||
|
||||
@SerializedName("phone_number")
|
||||
private String phoneNumber;
|
||||
|
||||
@SerializedName("iccid")
|
||||
private String iccid;
|
||||
|
||||
@SerializedName("imsi")
|
||||
private String imsi;
|
||||
|
||||
@SerializedName("camera_info")
|
||||
private String cameraInfo;
|
||||
|
||||
@SerializedName("sensor_count")
|
||||
private Integer sensorCount;
|
||||
|
||||
@SerializedName("app_list_md5")
|
||||
private String appListMd5;
|
||||
|
||||
@SerializedName("is_charging")
|
||||
private Integer isCharging;
|
||||
|
||||
@SerializedName("charging_type")
|
||||
private String chargingType;
|
||||
|
||||
@SerializedName("temperature")
|
||||
private String temperature;
|
||||
|
||||
@SerializedName("free_storage")
|
||||
private String freeStorage;
|
||||
|
||||
@SerializedName("total_storage")
|
||||
private String totalStorage;
|
||||
|
||||
@SerializedName("free_ram")
|
||||
private String freeRam;
|
||||
|
||||
@SerializedName("total_ram")
|
||||
private String totalRam;
|
||||
|
||||
public String getAppVersion() {
|
||||
return appVersion;
|
||||
}
|
||||
|
||||
public void setAppVersion(String appVersion) {
|
||||
this.appVersion = appVersion;
|
||||
}
|
||||
|
||||
public Integer getAppVersionCode() {
|
||||
return appVersionCode;
|
||||
}
|
||||
|
||||
public void setAppVersionCode(Integer appVersionCode) {
|
||||
this.appVersionCode = appVersionCode;
|
||||
}
|
||||
|
||||
public String getInstallTime() {
|
||||
return installTime;
|
||||
}
|
||||
|
||||
public void setInstallTime(String installTime) {
|
||||
this.installTime = installTime;
|
||||
}
|
||||
|
||||
public String getUpdateTimeApp() {
|
||||
return updateTimeApp;
|
||||
}
|
||||
|
||||
public void setUpdateTimeApp(String updateTimeApp) {
|
||||
this.updateTimeApp = updateTimeApp;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public void setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
public String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public void setChannel(String channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getGaid() {
|
||||
return gaid;
|
||||
}
|
||||
|
||||
public void setGaid(String gaid) {
|
||||
this.gaid = gaid;
|
||||
}
|
||||
|
||||
public String getOaid() {
|
||||
return oaid;
|
||||
}
|
||||
|
||||
public void setOaid(String oaid) {
|
||||
this.oaid = oaid;
|
||||
}
|
||||
|
||||
public String getPhoneNumber() {
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
public void setPhoneNumber(String phoneNumber) {
|
||||
this.phoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
public String getIccid() {
|
||||
return iccid;
|
||||
}
|
||||
|
||||
public void setIccid(String iccid) {
|
||||
this.iccid = iccid;
|
||||
}
|
||||
|
||||
public String getImsi() {
|
||||
return imsi;
|
||||
}
|
||||
|
||||
public void setImsi(String imsi) {
|
||||
this.imsi = imsi;
|
||||
}
|
||||
|
||||
public String getCameraInfo() {
|
||||
return cameraInfo;
|
||||
}
|
||||
|
||||
public void setCameraInfo(String cameraInfo) {
|
||||
this.cameraInfo = cameraInfo;
|
||||
}
|
||||
|
||||
public Integer getSensorCount() {
|
||||
return sensorCount;
|
||||
}
|
||||
|
||||
public void setSensorCount(Integer sensorCount) {
|
||||
this.sensorCount = sensorCount;
|
||||
}
|
||||
|
||||
public String getAppListMd5() {
|
||||
return appListMd5;
|
||||
}
|
||||
|
||||
public void setAppListMd5(String appListMd5) {
|
||||
this.appListMd5 = appListMd5;
|
||||
}
|
||||
|
||||
public Integer getIsCharging() {
|
||||
return isCharging;
|
||||
}
|
||||
|
||||
public void setIsCharging(Integer isCharging) {
|
||||
this.isCharging = isCharging;
|
||||
}
|
||||
|
||||
public String getChargingType() {
|
||||
return chargingType;
|
||||
}
|
||||
|
||||
public void setChargingType(String chargingType) {
|
||||
this.chargingType = chargingType;
|
||||
}
|
||||
|
||||
public String getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public void setTemperature(String temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
public String getFreeStorage() {
|
||||
return freeStorage;
|
||||
}
|
||||
|
||||
public void setFreeStorage(String freeStorage) {
|
||||
this.freeStorage = freeStorage;
|
||||
}
|
||||
|
||||
public String getTotalStorage() {
|
||||
return totalStorage;
|
||||
}
|
||||
|
||||
public void setTotalStorage(String totalStorage) {
|
||||
this.totalStorage = totalStorage;
|
||||
}
|
||||
|
||||
public String getFreeRam() {
|
||||
return freeRam;
|
||||
}
|
||||
|
||||
public void setFreeRam(String freeRam) {
|
||||
this.freeRam = freeRam;
|
||||
}
|
||||
|
||||
public String getTotalRam() {
|
||||
return totalRam;
|
||||
}
|
||||
|
||||
public void setTotalRam(String totalRam) {
|
||||
this.totalRam = totalRam;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
package com.ttstd.dialer.bean.req;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 设备安全信息上传请求。
|
||||
* <p>
|
||||
* 对应后端 {@code POST /api/v1/sn/update_security_info},
|
||||
* 字段与后端 {@code SnSecurityInfoReq} 一一对应。
|
||||
*/
|
||||
public class SnSecurityInfoReq implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SerializedName("is_root")
|
||||
private Integer isRoot;
|
||||
|
||||
@SerializedName("is_jailbreak")
|
||||
private Integer isJailbreak;
|
||||
|
||||
@SerializedName("root_status")
|
||||
private Integer rootStatus;
|
||||
|
||||
@SerializedName("has_su_binary")
|
||||
private Integer hasSuBinary;
|
||||
|
||||
@SerializedName("has_busybox")
|
||||
private Integer hasBusybox;
|
||||
|
||||
@SerializedName("is_emulator")
|
||||
private Integer isEmulator;
|
||||
|
||||
@SerializedName("is_debuggable")
|
||||
private Integer isDebuggable;
|
||||
|
||||
@SerializedName("is_usb_debug")
|
||||
private Integer isUsbDebug;
|
||||
|
||||
@SerializedName("is_unknown_sources")
|
||||
private Integer isUnknownSources;
|
||||
|
||||
@SerializedName("is_verify_apps")
|
||||
private Integer isVerifyApps;
|
||||
|
||||
@SerializedName("is_encrypted")
|
||||
private Integer isEncrypted;
|
||||
|
||||
@SerializedName("lock_screen_type")
|
||||
private String lockScreenType;
|
||||
|
||||
@SerializedName("has_password")
|
||||
private Integer hasPassword;
|
||||
|
||||
@SerializedName("password_type")
|
||||
private String passwordType;
|
||||
|
||||
@SerializedName("security_patch")
|
||||
private String securityPatch;
|
||||
|
||||
@SerializedName("google_play_protect")
|
||||
private Integer googlePlayProtect;
|
||||
|
||||
@SerializedName("adb_status")
|
||||
private Integer adbStatus;
|
||||
|
||||
@SerializedName("is_vpn_active")
|
||||
private Integer isVpnActive;
|
||||
|
||||
@SerializedName("is_device_owner")
|
||||
private Integer isDeviceOwner;
|
||||
|
||||
@SerializedName("android_version")
|
||||
private String androidVersion;
|
||||
|
||||
@SerializedName("kernel_version")
|
||||
private String kernelVersion;
|
||||
|
||||
public Integer getIsRoot() {
|
||||
return isRoot;
|
||||
}
|
||||
|
||||
public void setIsRoot(Integer isRoot) {
|
||||
this.isRoot = isRoot;
|
||||
}
|
||||
|
||||
public Integer getIsJailbreak() {
|
||||
return isJailbreak;
|
||||
}
|
||||
|
||||
public void setIsJailbreak(Integer isJailbreak) {
|
||||
this.isJailbreak = isJailbreak;
|
||||
}
|
||||
|
||||
public Integer getRootStatus() {
|
||||
return rootStatus;
|
||||
}
|
||||
|
||||
public void setRootStatus(Integer rootStatus) {
|
||||
this.rootStatus = rootStatus;
|
||||
}
|
||||
|
||||
public Integer getHasSuBinary() {
|
||||
return hasSuBinary;
|
||||
}
|
||||
|
||||
public void setHasSuBinary(Integer hasSuBinary) {
|
||||
this.hasSuBinary = hasSuBinary;
|
||||
}
|
||||
|
||||
public Integer getHasBusybox() {
|
||||
return hasBusybox;
|
||||
}
|
||||
|
||||
public void setHasBusybox(Integer hasBusybox) {
|
||||
this.hasBusybox = hasBusybox;
|
||||
}
|
||||
|
||||
public Integer getIsEmulator() {
|
||||
return isEmulator;
|
||||
}
|
||||
|
||||
public void setIsEmulator(Integer isEmulator) {
|
||||
this.isEmulator = isEmulator;
|
||||
}
|
||||
|
||||
public Integer getIsDebuggable() {
|
||||
return isDebuggable;
|
||||
}
|
||||
|
||||
public void setIsDebuggable(Integer isDebuggable) {
|
||||
this.isDebuggable = isDebuggable;
|
||||
}
|
||||
|
||||
public Integer getIsUsbDebug() {
|
||||
return isUsbDebug;
|
||||
}
|
||||
|
||||
public void setIsUsbDebug(Integer isUsbDebug) {
|
||||
this.isUsbDebug = isUsbDebug;
|
||||
}
|
||||
|
||||
public Integer getIsUnknownSources() {
|
||||
return isUnknownSources;
|
||||
}
|
||||
|
||||
public void setIsUnknownSources(Integer isUnknownSources) {
|
||||
this.isUnknownSources = isUnknownSources;
|
||||
}
|
||||
|
||||
public Integer getIsVerifyApps() {
|
||||
return isVerifyApps;
|
||||
}
|
||||
|
||||
public void setIsVerifyApps(Integer isVerifyApps) {
|
||||
this.isVerifyApps = isVerifyApps;
|
||||
}
|
||||
|
||||
public Integer getIsEncrypted() {
|
||||
return isEncrypted;
|
||||
}
|
||||
|
||||
public void setIsEncrypted(Integer isEncrypted) {
|
||||
this.isEncrypted = isEncrypted;
|
||||
}
|
||||
|
||||
public String getLockScreenType() {
|
||||
return lockScreenType;
|
||||
}
|
||||
|
||||
public void setLockScreenType(String lockScreenType) {
|
||||
this.lockScreenType = lockScreenType;
|
||||
}
|
||||
|
||||
public Integer getHasPassword() {
|
||||
return hasPassword;
|
||||
}
|
||||
|
||||
public void setHasPassword(Integer hasPassword) {
|
||||
this.hasPassword = hasPassword;
|
||||
}
|
||||
|
||||
public String getPasswordType() {
|
||||
return passwordType;
|
||||
}
|
||||
|
||||
public void setPasswordType(String passwordType) {
|
||||
this.passwordType = passwordType;
|
||||
}
|
||||
|
||||
public String getSecurityPatch() {
|
||||
return securityPatch;
|
||||
}
|
||||
|
||||
public void setSecurityPatch(String securityPatch) {
|
||||
this.securityPatch = securityPatch;
|
||||
}
|
||||
|
||||
public Integer getGooglePlayProtect() {
|
||||
return googlePlayProtect;
|
||||
}
|
||||
|
||||
public void setGooglePlayProtect(Integer googlePlayProtect) {
|
||||
this.googlePlayProtect = googlePlayProtect;
|
||||
}
|
||||
|
||||
public Integer getAdbStatus() {
|
||||
return adbStatus;
|
||||
}
|
||||
|
||||
public void setAdbStatus(Integer adbStatus) {
|
||||
this.adbStatus = adbStatus;
|
||||
}
|
||||
|
||||
public Integer getIsVpnActive() {
|
||||
return isVpnActive;
|
||||
}
|
||||
|
||||
public void setIsVpnActive(Integer isVpnActive) {
|
||||
this.isVpnActive = isVpnActive;
|
||||
}
|
||||
|
||||
public Integer getIsDeviceOwner() {
|
||||
return isDeviceOwner;
|
||||
}
|
||||
|
||||
public void setIsDeviceOwner(Integer isDeviceOwner) {
|
||||
this.isDeviceOwner = isDeviceOwner;
|
||||
}
|
||||
|
||||
public String getAndroidVersion() {
|
||||
return androidVersion;
|
||||
}
|
||||
|
||||
public void setAndroidVersion(String androidVersion) {
|
||||
this.androidVersion = androidVersion;
|
||||
}
|
||||
|
||||
public String getKernelVersion() {
|
||||
return kernelVersion;
|
||||
}
|
||||
|
||||
public void setKernelVersion(String kernelVersion) {
|
||||
this.kernelVersion = kernelVersion;
|
||||
}
|
||||
}
|
||||
151
app/src/main/java/com/ttstd/dialer/bean/req/SnSystemInfoReq.java
Normal file
151
app/src/main/java/com/ttstd/dialer/bean/req/SnSystemInfoReq.java
Normal file
@@ -0,0 +1,151 @@
|
||||
package com.ttstd.dialer.bean.req;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 设备基本信息上传请求。
|
||||
* <p>
|
||||
* 对应后端 {@code POST /api/v1/sn/update_system_info},
|
||||
* 字段与后端 {@code SnSystemInfoReq} 一一对应。
|
||||
* <p>
|
||||
* 注意:该类原名为 SnHardwareInfoReq,但其字段(sn_imei/sn_model/sn_brand 等)
|
||||
* 实际对应的是后端的「基本信息」而非「硬件信息」,故更名以避免与新增的
|
||||
* {@link SnHardwareInfoReq}(CPU/内存/电池/GPU)混淆。
|
||||
*/
|
||||
public class SnSystemInfoReq implements Serializable {
|
||||
private static final long serialVersionUID = 2728925136862797858L;
|
||||
|
||||
@SerializedName("sn_imei")
|
||||
private String imei;
|
||||
|
||||
@SerializedName("sn_imsi")
|
||||
private String imsi;
|
||||
|
||||
@SerializedName("sn_wlan_mac")
|
||||
private String wlanMac;
|
||||
|
||||
@SerializedName("sn_device_mac")
|
||||
private String deviceMac;
|
||||
|
||||
@SerializedName("sn_bluetooth_mac")
|
||||
private String bluetoothMac;
|
||||
|
||||
@SerializedName("sn_model")
|
||||
private String model;
|
||||
|
||||
@SerializedName("sn_brand")
|
||||
private String brand;
|
||||
|
||||
@SerializedName("sn_board")
|
||||
private String board;
|
||||
|
||||
@SerializedName("sn_android_version")
|
||||
private String androidVersion;
|
||||
|
||||
@SerializedName("sn_android_api")
|
||||
private int androidApi;
|
||||
|
||||
@SerializedName("sn_build_id")
|
||||
private String buildId;
|
||||
|
||||
@SerializedName("sn_build_display_id")
|
||||
private String buildDisplayId;
|
||||
|
||||
public String getImei() {
|
||||
return imei;
|
||||
}
|
||||
|
||||
public void setImei(String imei) {
|
||||
this.imei = imei;
|
||||
}
|
||||
|
||||
public String getImsi() {
|
||||
return imsi;
|
||||
}
|
||||
|
||||
public void setImsi(String imsi) {
|
||||
this.imsi = imsi;
|
||||
}
|
||||
|
||||
public String getWlanMac() {
|
||||
return wlanMac;
|
||||
}
|
||||
|
||||
public void setWlanMac(String wlanMac) {
|
||||
this.wlanMac = wlanMac;
|
||||
}
|
||||
|
||||
public String getDeviceMac() {
|
||||
return deviceMac;
|
||||
}
|
||||
|
||||
public void setDeviceMac(String deviceMac) {
|
||||
this.deviceMac = deviceMac;
|
||||
}
|
||||
|
||||
public String getBluetoothMac() {
|
||||
return bluetoothMac;
|
||||
}
|
||||
|
||||
public void setBluetoothMac(String bluetoothMac) {
|
||||
this.bluetoothMac = bluetoothMac;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public String getBrand() {
|
||||
return brand;
|
||||
}
|
||||
|
||||
public void setBrand(String brand) {
|
||||
this.brand = brand;
|
||||
}
|
||||
|
||||
public String getBoard() {
|
||||
return board;
|
||||
}
|
||||
|
||||
public void setBoard(String board) {
|
||||
this.board = board;
|
||||
}
|
||||
|
||||
public String getAndroidVersion() {
|
||||
return androidVersion;
|
||||
}
|
||||
|
||||
public void setAndroidVersion(String androidVersion) {
|
||||
this.androidVersion = androidVersion;
|
||||
}
|
||||
|
||||
public int getAndroidApi() {
|
||||
return androidApi;
|
||||
}
|
||||
|
||||
public void setAndroidApi(int androidApi) {
|
||||
this.androidApi = androidApi;
|
||||
}
|
||||
|
||||
public String getBuildId() {
|
||||
return buildId;
|
||||
}
|
||||
|
||||
public void setBuildId(String buildId) {
|
||||
this.buildId = buildId;
|
||||
}
|
||||
|
||||
public String getBuildDisplayId() {
|
||||
return buildDisplayId;
|
||||
}
|
||||
|
||||
public void setBuildDisplayId(String buildDisplayId) {
|
||||
this.buildDisplayId = buildDisplayId;
|
||||
}
|
||||
}
|
||||
876
app/src/main/java/com/ttstd/dialer/mdm/DeviceInfoCollector.java
Normal file
876
app/src/main/java/com/ttstd/dialer/mdm/DeviceInfoCollector.java
Normal file
@@ -0,0 +1,876 @@
|
||||
package com.ttstd.dialer.mdm;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorManager;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.StatFs;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.ttstd.dialer.bean.req.SnHardwareInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnNetworkInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnOtherInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnSecurityInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnSystemInfoReq;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 设备信息采集器。
|
||||
* <p>
|
||||
* 负责采集后端 5 个上传接口所需的全部信息:
|
||||
* <ul>
|
||||
* <li>{@link #collectSystemInfo()} —— 基本信息</li>
|
||||
* <li>{@link #collectHardwareInfo()} —— 硬件信息(CPU/内存/存储/电池/传感器)</li>
|
||||
* <li>{@link #collectNetworkInfo()} —— 网络信息(WiFi/移动网络/蓝牙/代理)</li>
|
||||
* <li>{@link #collectSecurityInfo()} —— 安全信息(Root/调试/加密/补丁)</li>
|
||||
* <li>{@link #collectOtherInfo()} —— 其他信息(App 版本/存储/电池快照)</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 设计要点:
|
||||
* <ol>
|
||||
* <li>每个字段的采集都独立 try/catch,单个字段失败不影响整体上报;</li>
|
||||
* <li>采集不到的字段保持 null,Gson 默认不序列化 null,避免覆盖服务端已有数据;</li>
|
||||
* <li>涉及权限的调用(IMEI/IMSI/ICCID/手机号)均做权限检查,无权限时静默跳过;</li>
|
||||
* <li>存在文件读取与 shell 探测,耗时较长,务必在子线程调用。</li>
|
||||
* </ol>
|
||||
*/
|
||||
public class DeviceInfoCollector {
|
||||
|
||||
private static final String TAG = "DeviceInfoCollector";
|
||||
|
||||
/**
|
||||
* 后端 LocalDateTime 默认接受的 ISO 格式。
|
||||
*/
|
||||
private static final String DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
|
||||
|
||||
/**
|
||||
* 常见的 su 二进制路径,用于 Root 检测。
|
||||
*/
|
||||
private static final String[] SU_PATHS = {
|
||||
"/system/bin/su",
|
||||
"/system/xbin/su",
|
||||
"/sbin/su",
|
||||
"/system/su",
|
||||
"/vendor/bin/su",
|
||||
"/su/bin/su",
|
||||
"/data/local/xbin/su",
|
||||
"/data/local/bin/su",
|
||||
"/system/sd/xbin/su"
|
||||
};
|
||||
|
||||
/**
|
||||
* 常见的 busybox 路径。
|
||||
*/
|
||||
private static final String[] BUSYBOX_PATHS = {
|
||||
"/system/bin/busybox",
|
||||
"/system/xbin/busybox",
|
||||
"/sbin/busybox",
|
||||
"/vendor/bin/busybox",
|
||||
"/data/local/xbin/busybox"
|
||||
};
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
public DeviceInfoCollector(Context context) {
|
||||
this.mContext = context.getApplicationContext();
|
||||
}
|
||||
|
||||
// ==================================================================
|
||||
// 基本信息
|
||||
// ==================================================================
|
||||
|
||||
/**
|
||||
* 采集设备基本信息。
|
||||
*/
|
||||
public SnSystemInfoReq collectSystemInfo() {
|
||||
SnSystemInfoReq req = new SnSystemInfoReq();
|
||||
DeviceManagerService dms = DeviceManagerService.getInstance();
|
||||
|
||||
req.setImei(safeString(dms::getImei));
|
||||
req.setImsi(safeString(dms::getImsi));
|
||||
req.setWlanMac(safeString(dms::getWlanMac));
|
||||
req.setDeviceMac(safeString(dms::getDeviceMac));
|
||||
req.setBluetoothMac(safeString(dms::getBluetoothMac));
|
||||
|
||||
req.setModel(Build.MODEL);
|
||||
req.setBrand(Build.BRAND);
|
||||
req.setBoard(Build.BOARD);
|
||||
req.setAndroidVersion(Build.VERSION.RELEASE);
|
||||
req.setAndroidApi(Build.VERSION.SDK_INT);
|
||||
req.setBuildId(Build.ID);
|
||||
req.setBuildDisplayId(Build.DISPLAY);
|
||||
return req;
|
||||
}
|
||||
|
||||
// ==================================================================
|
||||
// 硬件信息
|
||||
// ==================================================================
|
||||
|
||||
/**
|
||||
* 采集设备硬件信息。
|
||||
*/
|
||||
public SnHardwareInfoReq collectHardwareInfo() {
|
||||
SnHardwareInfoReq req = new SnHardwareInfoReq();
|
||||
|
||||
// ---- CPU ----
|
||||
try {
|
||||
req.setCpuAbi(Build.SUPPORTED_ABIS.length > 0 ? Build.SUPPORTED_ABIS[0] : null);
|
||||
req.setCpuAbi2(Build.SUPPORTED_ABIS.length > 1 ? Build.SUPPORTED_ABIS[1] : null);
|
||||
req.setSupportAbi(join(Build.SUPPORTED_32_BIT_ABIS));
|
||||
req.setSupportAbi64(join(Build.SUPPORTED_64_BIT_ABIS));
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect cpu abi failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
req.setCpuCores(Runtime.getRuntime().availableProcessors());
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect cpu cores failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
req.setCpuModel(readCpuModel());
|
||||
req.setCpuMin(Build.HARDWARE);
|
||||
req.setCpuMaxFreq(readCpuFreq("cpuinfo_max_freq"));
|
||||
req.setCpuMinFreq(readCpuFreq("cpuinfo_min_freq"));
|
||||
|
||||
// ---- 内存 ----
|
||||
try {
|
||||
ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
if (am != null) {
|
||||
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
|
||||
am.getMemoryInfo(mi);
|
||||
req.setTotalRam(formatSize(mi.totalMem));
|
||||
req.setAvailableRam(formatSize(mi.availMem));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect ram failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// ---- 存储 ----
|
||||
try {
|
||||
File data = Environment.getDataDirectory();
|
||||
StatFs stat = new StatFs(data.getPath());
|
||||
long total = stat.getBlockCountLong() * stat.getBlockSizeLong();
|
||||
long available = stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
|
||||
req.setInternalStorage(formatSize(total));
|
||||
req.setAvailableStorage(formatSize(available));
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect storage failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
|
||||
File ext = Environment.getExternalStorageDirectory();
|
||||
StatFs stat = new StatFs(ext.getPath());
|
||||
long total = stat.getBlockCountLong() * stat.getBlockSizeLong();
|
||||
req.setExternalStorage(formatSize(total));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect external storage failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// ---- 电池 ----
|
||||
try {
|
||||
Intent battery = mContext.registerReceiver(null,
|
||||
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
if (battery != null) {
|
||||
int level = battery.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
|
||||
int scale = battery.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
|
||||
if (level >= 0 && scale > 0) {
|
||||
req.setBatteryLevel(level * 100 / scale);
|
||||
}
|
||||
req.setBatteryStatus(batteryStatusName(
|
||||
battery.getIntExtra(BatteryManager.EXTRA_STATUS, -1)));
|
||||
req.setBatteryHealth(batteryHealthName(
|
||||
battery.getIntExtra(BatteryManager.EXTRA_HEALTH, -1)));
|
||||
|
||||
int temp = battery.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1);
|
||||
if (temp > 0) {
|
||||
// 系统返回的是 0.1℃ 为单位的整数
|
||||
req.setBatteryTemp(String.valueOf(temp / 10.0f));
|
||||
}
|
||||
int voltage = battery.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1);
|
||||
if (voltage > 0) {
|
||||
req.setBatteryVoltage(voltage + "mV");
|
||||
}
|
||||
req.setBatteryTechnology(
|
||||
battery.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY));
|
||||
}
|
||||
req.setBatteryCapacity(readBatteryCapacity());
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect battery failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// ---- 传感器 ----
|
||||
try {
|
||||
SensorManager sm = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
|
||||
if (sm != null) {
|
||||
List<Sensor> sensors = sm.getSensorList(Sensor.TYPE_ALL);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Sensor s : sensors) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(s.getName());
|
||||
}
|
||||
req.setSensors(sb.length() > 0 ? sb.toString() : null);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect sensors failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// GPU 信息需要 GL 上下文,普通 Service 中无法可靠获取,此处留空由服务端容错。
|
||||
return req;
|
||||
}
|
||||
|
||||
// ==================================================================
|
||||
// 网络信息
|
||||
// ==================================================================
|
||||
|
||||
/**
|
||||
* 采集设备网络信息。
|
||||
*/
|
||||
public SnNetworkInfoReq collectNetworkInfo() {
|
||||
SnNetworkInfoReq req = new SnNetworkInfoReq();
|
||||
|
||||
ConnectivityManager cm =
|
||||
(ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
|
||||
// ---- 连接状态与网络类型 ----
|
||||
try {
|
||||
if (cm != null) {
|
||||
Network active = cm.getActiveNetwork();
|
||||
NetworkCapabilities caps = active == null ? null : cm.getNetworkCapabilities(active);
|
||||
boolean connected = caps != null
|
||||
&& caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
|
||||
req.setIsConnected(connected ? 1 : 0);
|
||||
|
||||
if (caps != null) {
|
||||
if (caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
|
||||
req.setNetworkType("WIFI");
|
||||
} else if (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
|
||||
req.setNetworkType("MOBILE");
|
||||
} else if (caps.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
|
||||
req.setNetworkType("ETHERNET");
|
||||
} else {
|
||||
req.setNetworkType("OTHER");
|
||||
}
|
||||
req.setIsVpn(caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN) ? 1 : 0);
|
||||
}
|
||||
|
||||
// DNS 与网关
|
||||
LinkProperties lp = active == null ? null : cm.getLinkProperties(active);
|
||||
if (lp != null) {
|
||||
StringBuilder dns = new StringBuilder();
|
||||
for (InetAddress addr : lp.getDnsServers()) {
|
||||
if (dns.length() > 0) {
|
||||
dns.append(",");
|
||||
}
|
||||
dns.append(addr.getHostAddress());
|
||||
}
|
||||
req.setWifiDns(dns.length() > 0 ? dns.toString() : null);
|
||||
|
||||
for (LinkAddress la : lp.getLinkAddresses()) {
|
||||
if (la.getAddress() instanceof Inet4Address) {
|
||||
req.setWifiNetmask(String.valueOf(la.getPrefixLength()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect network state failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// ---- WiFi ----
|
||||
try {
|
||||
WifiManager wm = (WifiManager) mContext.getApplicationContext()
|
||||
.getSystemService(Context.WIFI_SERVICE);
|
||||
if (wm != null) {
|
||||
WifiInfo info = wm.getConnectionInfo();
|
||||
if (info != null) {
|
||||
String ssid = info.getSSID();
|
||||
if (!TextUtils.isEmpty(ssid)) {
|
||||
// 系统返回的 SSID 常带引号
|
||||
req.setWifiSsid(ssid.replace("\"", ""));
|
||||
}
|
||||
req.setWifiBssid(info.getBSSID());
|
||||
req.setWifiRssi(info.getRssi());
|
||||
req.setWifiSpeed(info.getLinkSpeed() + "Mbps");
|
||||
|
||||
int ip = info.getIpAddress();
|
||||
if (ip != 0) {
|
||||
req.setWifiIp(intToIp(ip));
|
||||
}
|
||||
}
|
||||
// Android 6.0+ 出于隐私限制,getMacAddress 返回固定值,改用 MDM 接口
|
||||
req.setWifiMac(safeString(() -> DeviceManagerService.getInstance().getWlanMac()));
|
||||
|
||||
android.net.DhcpInfo dhcp = wm.getDhcpInfo();
|
||||
if (dhcp != null && dhcp.gateway != 0) {
|
||||
req.setWifiGateway(intToIp(dhcp.gateway));
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect wifi failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// ---- 移动网络 ----
|
||||
try {
|
||||
TelephonyManager tm =
|
||||
(TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if (tm != null) {
|
||||
req.setMobileOperator(tm.getNetworkOperatorName());
|
||||
req.setMobileNetworkType(networkTypeName(tm));
|
||||
if (hasPermission(Manifest.permission.READ_PHONE_STATE)) {
|
||||
req.setMobileSimSerial(safeString(tm::getSimSerialNumber));
|
||||
}
|
||||
}
|
||||
// IMEI/IMSI 走 MDM 接口,规避高版本系统的权限限制
|
||||
req.setMobileImei(safeString(() -> DeviceManagerService.getInstance().getImei()));
|
||||
req.setMobileImsi(safeString(() -> DeviceManagerService.getInstance().getImsi()));
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect mobile failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// ---- 蓝牙 ----
|
||||
try {
|
||||
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (adapter != null) {
|
||||
req.setBluetoothName(safeString(adapter::getName));
|
||||
}
|
||||
req.setBluetoothMac(safeString(() -> DeviceManagerService.getInstance().getBluetoothMac()));
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect bluetooth failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// ---- 代理 ----
|
||||
try {
|
||||
String host = System.getProperty("http.proxyHost");
|
||||
String port = System.getProperty("http.proxyPort");
|
||||
boolean hasProxy = !TextUtils.isEmpty(host);
|
||||
req.setIsProxy(hasProxy ? 1 : 0);
|
||||
if (hasProxy) {
|
||||
req.setProxyHost(host);
|
||||
req.setProxyPort(port);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect proxy failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
// ==================================================================
|
||||
// 安全信息
|
||||
// ==================================================================
|
||||
|
||||
/**
|
||||
* 采集设备安全信息。
|
||||
*/
|
||||
public SnSecurityInfoReq collectSecurityInfo() {
|
||||
SnSecurityInfoReq req = new SnSecurityInfoReq();
|
||||
|
||||
// ---- Root 检测 ----
|
||||
int hasSu = anyFileExists(SU_PATHS) ? 1 : 0;
|
||||
int hasBusybox = anyFileExists(BUSYBOX_PATHS) ? 1 : 0;
|
||||
int testKeys = Build.TAGS != null && Build.TAGS.contains("test-keys") ? 1 : 0;
|
||||
int rooted = (hasSu == 1 || hasBusybox == 1 || testKeys == 1) ? 1 : 0;
|
||||
|
||||
req.setHasSuBinary(hasSu);
|
||||
req.setHasBusybox(hasBusybox);
|
||||
req.setIsRoot(rooted);
|
||||
req.setIsJailbreak(rooted);
|
||||
req.setRootStatus(rooted);
|
||||
|
||||
// ---- 模拟器检测 ----
|
||||
req.setIsEmulator(isEmulator() ? 1 : 0);
|
||||
|
||||
// ---- 调试相关 ----
|
||||
try {
|
||||
ApplicationInfo ai = mContext.getApplicationInfo();
|
||||
req.setIsDebuggable((ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 ? 1 : 0);
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect debuggable failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
int adb = Settings.Global.getInt(mContext.getContentResolver(),
|
||||
Settings.Global.ADB_ENABLED, 0);
|
||||
req.setIsUsbDebug(adb);
|
||||
req.setAdbStatus(adb);
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect adb failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
// INSTALL_NON_MARKET_APPS 在 Android 8.0 后被弃用,改为按应用授权
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
req.setIsUnknownSources(Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.INSTALL_NON_MARKET_APPS, 0));
|
||||
} else {
|
||||
req.setIsUnknownSources(
|
||||
mContext.getPackageManager().canRequestPackageInstalls() ? 1 : 0);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect unknown sources failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
req.setIsVerifyApps(Settings.Global.getInt(mContext.getContentResolver(),
|
||||
"package_verifier_enable", 0));
|
||||
req.setGooglePlayProtect(req.getIsVerifyApps());
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect verify apps failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// ---- 加密与锁屏 ----
|
||||
try {
|
||||
DevicePolicyManager dpm =
|
||||
(DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
|
||||
if (dpm != null) {
|
||||
int status = dpm.getStorageEncryptionStatus();
|
||||
boolean encrypted =
|
||||
status == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE
|
||||
|| status == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER
|
||||
|| status == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY;
|
||||
req.setIsEncrypted(encrypted ? 1 : 0);
|
||||
req.setIsDeviceOwner(dpm.isDeviceOwnerApp(mContext.getPackageName()) ? 1 : 0);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect encryption failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
android.app.KeyguardManager km =
|
||||
(android.app.KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
|
||||
if (km != null) {
|
||||
boolean secure = km.isDeviceSecure();
|
||||
req.setHasPassword(secure ? 1 : 0);
|
||||
req.setLockScreenType(secure ? "SECURE" : "NONE");
|
||||
req.setPasswordType(secure ? "SECURE" : "NONE");
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect keyguard failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// ---- VPN ----
|
||||
try {
|
||||
ConnectivityManager cm =
|
||||
(ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (cm != null) {
|
||||
Network active = cm.getActiveNetwork();
|
||||
NetworkCapabilities caps = active == null ? null : cm.getNetworkCapabilities(active);
|
||||
req.setIsVpnActive(caps != null
|
||||
&& caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN) ? 1 : 0);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect vpn failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// ---- 版本信息 ----
|
||||
req.setAndroidVersion(Build.VERSION.RELEASE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
req.setSecurityPatch(Build.VERSION.SECURITY_PATCH);
|
||||
}
|
||||
req.setKernelVersion(System.getProperty("os.version"));
|
||||
return req;
|
||||
}
|
||||
|
||||
// ==================================================================
|
||||
// 其他信息
|
||||
// ==================================================================
|
||||
|
||||
/**
|
||||
* 采集设备其他信息。
|
||||
*/
|
||||
public SnOtherInfoReq collectOtherInfo() {
|
||||
SnOtherInfoReq req = new SnOtherInfoReq();
|
||||
|
||||
// ---- App 自身信息 ----
|
||||
try {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
String pkg = mContext.getPackageName();
|
||||
PackageInfo pi = pm.getPackageInfo(pkg, 0);
|
||||
|
||||
req.setPackageName(pkg);
|
||||
req.setAppVersion(pi.versionName);
|
||||
req.setAppVersionCode(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
|
||||
? (int) pi.getLongVersionCode()
|
||||
: pi.versionCode);
|
||||
req.setInstallTime(formatDateTime(pi.firstInstallTime));
|
||||
req.setUpdateTimeApp(formatDateTime(pi.lastUpdateTime));
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect app info failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// ---- 设备标识 ----
|
||||
try {
|
||||
req.setDeviceId(Settings.Secure.getString(
|
||||
mContext.getContentResolver(), Settings.Secure.ANDROID_ID));
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect android id failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
req.setImsi(safeString(() -> DeviceManagerService.getInstance().getImsi()));
|
||||
|
||||
// ---- SIM 相关(需要权限,无权限则跳过)----
|
||||
try {
|
||||
if (hasPermission(Manifest.permission.READ_PHONE_STATE)) {
|
||||
TelephonyManager tm =
|
||||
(TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if (tm != null) {
|
||||
req.setIccid(safeString(tm::getSimSerialNumber));
|
||||
req.setPhoneNumber(safeString(tm::getLine1Number));
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect sim info failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// ---- 相机与传感器 ----
|
||||
try {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
boolean back = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
|
||||
boolean front = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
|
||||
req.setCameraInfo("back=" + back + ",front=" + front);
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect camera failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
SensorManager sm = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
|
||||
if (sm != null) {
|
||||
req.setSensorCount(sm.getSensorList(Sensor.TYPE_ALL).size());
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect sensor count failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// ---- 电池快照 ----
|
||||
try {
|
||||
Intent battery = mContext.registerReceiver(null,
|
||||
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
if (battery != null) {
|
||||
int status = battery.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
|
||||
boolean charging = status == BatteryManager.BATTERY_STATUS_CHARGING
|
||||
|| status == BatteryManager.BATTERY_STATUS_FULL;
|
||||
req.setIsCharging(charging ? 1 : 0);
|
||||
|
||||
int plugged = battery.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
|
||||
req.setChargingType(chargingTypeName(plugged));
|
||||
|
||||
int temp = battery.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1);
|
||||
if (temp > 0) {
|
||||
req.setTemperature(String.valueOf(temp / 10.0f));
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect battery snapshot failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
// ---- 存储与内存快照 ----
|
||||
try {
|
||||
StatFs stat = new StatFs(Environment.getDataDirectory().getPath());
|
||||
req.setTotalStorage(formatSize(stat.getBlockCountLong() * stat.getBlockSizeLong()));
|
||||
req.setFreeStorage(formatSize(stat.getAvailableBlocksLong() * stat.getBlockSizeLong()));
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect storage snapshot failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
if (am != null) {
|
||||
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
|
||||
am.getMemoryInfo(mi);
|
||||
req.setTotalRam(formatSize(mi.totalMem));
|
||||
req.setFreeRam(formatSize(mi.availMem));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "collect ram snapshot failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
// ==================================================================
|
||||
// 工具方法
|
||||
// ==================================================================
|
||||
|
||||
/**
|
||||
* 供 lambda 使用的取值接口,用于统一包裹异常。
|
||||
*/
|
||||
private interface StringSupplier {
|
||||
String get() throws Throwable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全取值:任何异常都返回 null,避免单个字段导致整体采集失败。
|
||||
*/
|
||||
private String safeString(StringSupplier supplier) {
|
||||
try {
|
||||
return supplier.get();
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "safeString failed: " + t.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasPermission(String permission) {
|
||||
try {
|
||||
return ContextCompat.checkSelfPermission(mContext, permission)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String join(String[] arr) {
|
||||
if (arr == null || arr.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return TextUtils.join(",", arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节数格式化为可读字符串,保留两位小数。
|
||||
*/
|
||||
private static String formatSize(long bytes) {
|
||||
if (bytes <= 0) {
|
||||
return null;
|
||||
}
|
||||
String[] units = {"B", "KB", "MB", "GB", "TB"};
|
||||
int idx = 0;
|
||||
double size = bytes;
|
||||
while (size >= 1024 && idx < units.length - 1) {
|
||||
size /= 1024;
|
||||
idx++;
|
||||
}
|
||||
return String.format(Locale.US, "%.2f%s", size, units[idx]);
|
||||
}
|
||||
|
||||
private static String formatDateTime(long millis) {
|
||||
if (millis <= 0) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new SimpleDateFormat(DATE_TIME_PATTERN, Locale.US).format(new Date(millis));
|
||||
} catch (Throwable t) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String intToIp(int ip) {
|
||||
return (ip & 0xFF) + "." + ((ip >> 8) & 0xFF) + "."
|
||||
+ ((ip >> 16) & 0xFF) + "." + ((ip >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
private static boolean anyFileExists(String[] paths) {
|
||||
for (String path : paths) {
|
||||
try {
|
||||
if (new File(path).exists()) {
|
||||
return true;
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
// 无权限访问时视为不存在
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 /proc/cpuinfo 读取 CPU 型号。
|
||||
*/
|
||||
private static String readCpuModel() {
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader("/proc/cpuinfo"))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.startsWith("Hardware") || line.startsWith("model name")) {
|
||||
int idx = line.indexOf(':');
|
||||
if (idx >= 0 && idx + 1 < line.length()) {
|
||||
return line.substring(idx + 1).trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "readCpuModel failed: " + t.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 CPU 频率(单位 KHz),转换为 MHz 字符串。
|
||||
*/
|
||||
private static String readCpuFreq(String fileName) {
|
||||
String path = "/sys/devices/system/cpu/cpu0/cpufreq/" + fileName;
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
|
||||
String value = reader.readLine();
|
||||
if (!TextUtils.isEmpty(value)) {
|
||||
long khz = Long.parseLong(value.trim());
|
||||
return (khz / 1000) + "MHz";
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// 部分设备该节点不可读,属正常情况
|
||||
Logger.e(TAG, "readCpuFreq " + fileName + " failed: " + t.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过反射读取电池设计容量(框架隐藏 API,失败返回 null)。
|
||||
*/
|
||||
private String readBatteryCapacity() {
|
||||
try {
|
||||
Object profile = Class.forName("com.android.internal.os.PowerProfile")
|
||||
.getConstructor(Context.class)
|
||||
.newInstance(mContext);
|
||||
Object capacity = Class.forName("com.android.internal.os.PowerProfile")
|
||||
.getMethod("getBatteryCapacity")
|
||||
.invoke(profile);
|
||||
if (capacity instanceof Double) {
|
||||
return String.format(Locale.US, "%.0fmAh", (Double) capacity);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "readBatteryCapacity failed: " + t.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isEmulator() {
|
||||
try {
|
||||
return Build.FINGERPRINT.startsWith("generic")
|
||||
|| Build.FINGERPRINT.startsWith("unknown")
|
||||
|| Build.MODEL.contains("google_sdk")
|
||||
|| Build.MODEL.contains("Emulator")
|
||||
|| Build.MODEL.contains("Android SDK built for x86")
|
||||
|| Build.MANUFACTURER.contains("Genymotion")
|
||||
|| (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
|
||||
|| "google_sdk".equals(Build.PRODUCT);
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String batteryStatusName(int status) {
|
||||
switch (status) {
|
||||
case BatteryManager.BATTERY_STATUS_CHARGING:
|
||||
return "CHARGING";
|
||||
case BatteryManager.BATTERY_STATUS_DISCHARGING:
|
||||
return "DISCHARGING";
|
||||
case BatteryManager.BATTERY_STATUS_FULL:
|
||||
return "FULL";
|
||||
case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
|
||||
return "NOT_CHARGING";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
private static String batteryHealthName(int health) {
|
||||
switch (health) {
|
||||
case BatteryManager.BATTERY_HEALTH_GOOD:
|
||||
return "GOOD";
|
||||
case BatteryManager.BATTERY_HEALTH_OVERHEAT:
|
||||
return "OVERHEAT";
|
||||
case BatteryManager.BATTERY_HEALTH_DEAD:
|
||||
return "DEAD";
|
||||
case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
|
||||
return "OVER_VOLTAGE";
|
||||
case BatteryManager.BATTERY_HEALTH_COLD:
|
||||
return "COLD";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
private static String chargingTypeName(int plugged) {
|
||||
switch (plugged) {
|
||||
case BatteryManager.BATTERY_PLUGGED_AC:
|
||||
return "AC";
|
||||
case BatteryManager.BATTERY_PLUGGED_USB:
|
||||
return "USB";
|
||||
case BatteryManager.BATTERY_PLUGGED_WIRELESS:
|
||||
return "WIRELESS";
|
||||
case 0:
|
||||
return "NONE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取移动网络制式名称,无权限时返回 null。
|
||||
*/
|
||||
private String networkTypeName(TelephonyManager tm) {
|
||||
try {
|
||||
int type;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
if (!hasPermission(Manifest.permission.READ_PHONE_STATE)) {
|
||||
return null;
|
||||
}
|
||||
type = tm.getDataNetworkType();
|
||||
} else {
|
||||
type = tm.getNetworkType();
|
||||
}
|
||||
switch (type) {
|
||||
case TelephonyManager.NETWORK_TYPE_GPRS:
|
||||
case TelephonyManager.NETWORK_TYPE_EDGE:
|
||||
case TelephonyManager.NETWORK_TYPE_CDMA:
|
||||
case TelephonyManager.NETWORK_TYPE_1xRTT:
|
||||
case TelephonyManager.NETWORK_TYPE_IDEN:
|
||||
return "2G";
|
||||
case TelephonyManager.NETWORK_TYPE_UMTS:
|
||||
case TelephonyManager.NETWORK_TYPE_EVDO_0:
|
||||
case TelephonyManager.NETWORK_TYPE_EVDO_A:
|
||||
case TelephonyManager.NETWORK_TYPE_HSDPA:
|
||||
case TelephonyManager.NETWORK_TYPE_HSUPA:
|
||||
case TelephonyManager.NETWORK_TYPE_HSPA:
|
||||
case TelephonyManager.NETWORK_TYPE_EVDO_B:
|
||||
case TelephonyManager.NETWORK_TYPE_EHRPD:
|
||||
case TelephonyManager.NETWORK_TYPE_HSPAP:
|
||||
return "3G";
|
||||
case TelephonyManager.NETWORK_TYPE_LTE:
|
||||
return "4G";
|
||||
case TelephonyManager.NETWORK_TYPE_NR:
|
||||
return "5G";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "networkTypeName failed: " + t.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,10 @@ import com.ttstd.dialer.bean.BaseResponse;
|
||||
import com.ttstd.dialer.bean.DeveloperOptions;
|
||||
import com.ttstd.dialer.bean.req.SnHardwareInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnLocationReq;
|
||||
import com.ttstd.dialer.bean.req.SnNetworkInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnOtherInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnSecurityInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnSystemInfoReq;
|
||||
import com.ttstd.dialer.db.contact.ContactInfo;
|
||||
import com.ttstd.dialer.mdm.DeviceManagerService;
|
||||
import com.ttstd.dialer.network.api.ContactApi;
|
||||
@@ -315,11 +319,46 @@ public class OkHttpManager {
|
||||
return schedule(getApiService(SnApi.class).register(DeviceManagerService.getInstance().getSerial(), Build.MODEL), provider);
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<Void>> getUpdateHardwareInfoObservable(SnHardwareInfoReq snHardwareInfoReq) {
|
||||
RequestBody body = convertToRequestBodyjson(snHardwareInfoReq);
|
||||
/**
|
||||
* 上传设备基本信息
|
||||
*/
|
||||
public Observable<BaseResponse<Void>> getUpdateSystemInfoObservable(SnSystemInfoReq req) {
|
||||
RequestBody body = convertToRequestBodyjson(req);
|
||||
return schedule(getApiService(SnApi.class).updateSystemInfo(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备硬件信息
|
||||
*/
|
||||
public Observable<BaseResponse<Void>> getUpdateHardwareInfoObservable(SnHardwareInfoReq req) {
|
||||
RequestBody body = convertToRequestBodyjson(req);
|
||||
return schedule(getApiService(SnApi.class).updateHardwareInfo(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备网络信息
|
||||
*/
|
||||
public Observable<BaseResponse<Void>> getUpdateNetworkInfoObservable(SnNetworkInfoReq req) {
|
||||
RequestBody body = convertToRequestBodyjson(req);
|
||||
return schedule(getApiService(SnApi.class).updateNetworkInfo(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备安全信息
|
||||
*/
|
||||
public Observable<BaseResponse<Void>> getUpdateSecurityInfoObservable(SnSecurityInfoReq req) {
|
||||
RequestBody body = convertToRequestBodyjson(req);
|
||||
return schedule(getApiService(SnApi.class).updateSecurityInfo(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备其他信息
|
||||
*/
|
||||
public Observable<BaseResponse<Void>> getUpdateOtherInfoObservable(SnOtherInfoReq req) {
|
||||
RequestBody body = convertToRequestBodyjson(req);
|
||||
return schedule(getApiService(SnApi.class).updateOtherInfo(body));
|
||||
}
|
||||
|
||||
public Observable<BaseResponse> getUploadScreenshotObservable(MultipartBody.Part part) {
|
||||
return schedule(getApiService(SnApi.class).uploadScreenshot(part));
|
||||
}
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
package com.ttstd.dialer.network;
|
||||
|
||||
public class UrlConstants {
|
||||
public static final String ROOT_URL = "http://192.168.100.111:8000/api/v1/sn/";
|
||||
public static final String ROOT_URL = "http://192.168.100.244:8000/api/v1/sn/";
|
||||
|
||||
//设备注册
|
||||
public static final String SN_REGISTER = "register";
|
||||
//上传硬件信息
|
||||
//上传设备基本信息
|
||||
public static final String UPDATE_SYSTEM_INFO = "update_system_info";
|
||||
//上传设备硬件信息
|
||||
public static final String UPDATE_HARDWARE_INFO = "update_hardware_info";
|
||||
//上传设备网络信息
|
||||
public static final String UPDATE_NETWORK_INFO = "update_network_info";
|
||||
//上传设备安全信息
|
||||
public static final String UPDATE_SECURITY_INFO = "update_security_info";
|
||||
//上传设备其他信息
|
||||
public static final String UPDATE_OTHER_INFO = "update_other_info";
|
||||
//上传屏幕截图
|
||||
public static final String UPLOAD_SCREENSHOT = "upload_screenshot";
|
||||
//上传定位地址
|
||||
|
||||
@@ -25,11 +25,46 @@ public interface SnApi {
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* 上传设备基本信息
|
||||
*/
|
||||
@POST(UrlConstants.UPDATE_SYSTEM_INFO)
|
||||
Observable<BaseResponse<Void>> updateSystemInfo(
|
||||
@Body RequestBody body
|
||||
);
|
||||
|
||||
/**
|
||||
* 上传设备硬件信息
|
||||
*/
|
||||
@POST(UrlConstants.UPDATE_HARDWARE_INFO)
|
||||
Observable<BaseResponse<Void>> updateHardwareInfo(
|
||||
@Body RequestBody body
|
||||
);
|
||||
|
||||
/**
|
||||
* 上传设备网络信息
|
||||
*/
|
||||
@POST(UrlConstants.UPDATE_NETWORK_INFO)
|
||||
Observable<BaseResponse<Void>> updateNetworkInfo(
|
||||
@Body RequestBody body
|
||||
);
|
||||
|
||||
/**
|
||||
* 上传设备安全信息
|
||||
*/
|
||||
@POST(UrlConstants.UPDATE_SECURITY_INFO)
|
||||
Observable<BaseResponse<Void>> updateSecurityInfo(
|
||||
@Body RequestBody body
|
||||
);
|
||||
|
||||
/**
|
||||
* 上传设备其他信息
|
||||
*/
|
||||
@POST(UrlConstants.UPDATE_OTHER_INFO)
|
||||
Observable<BaseResponse<Void>> updateOtherInfo(
|
||||
@Body RequestBody body
|
||||
);
|
||||
|
||||
@Multipart
|
||||
@POST(UrlConstants.UPLOAD_SCREENSHOT)
|
||||
Observable<BaseResponse> uploadScreenshot(
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.ttstd.dialer.push;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tencent.mmkv.MMKV;
|
||||
@@ -12,11 +11,11 @@ import com.ttstd.dialer.bean.ApkInstalledInfo;
|
||||
import com.ttstd.dialer.bean.BaseResponse;
|
||||
import com.ttstd.dialer.bean.DeveloperOptions;
|
||||
import com.ttstd.dialer.bean.PushMessage;
|
||||
import com.ttstd.dialer.bean.req.SnHardwareInfoReq;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.data.repository.SnRepository;
|
||||
import com.ttstd.dialer.manager.AppManager;
|
||||
import com.ttstd.dialer.manager.MapManager;
|
||||
import com.ttstd.dialer.mdm.DeviceInfoCollector;
|
||||
import com.ttstd.dialer.mdm.DeviceManagerService;
|
||||
import com.ttstd.dialer.network.BaseObserver;
|
||||
import com.ttstd.dialer.network.OkHttpManager;
|
||||
@@ -185,41 +184,68 @@ public class PushExecutor {
|
||||
});
|
||||
|
||||
|
||||
SnHardwareInfoReq snHardwareInfoReq = new SnHardwareInfoReq();
|
||||
snHardwareInfoReq.setImei(DeviceManagerService.getInstance().getImei());
|
||||
snHardwareInfoReq.setImsi(DeviceManagerService.getInstance().getImsi());
|
||||
snHardwareInfoReq.setWlanMac(DeviceManagerService.getInstance().getWlanMac());
|
||||
snHardwareInfoReq.setDeviceMac(DeviceManagerService.getInstance().getDeviceMac());
|
||||
snHardwareInfoReq.setBluetoothMac(DeviceManagerService.getInstance().getBluetoothMac());
|
||||
uploadAllDeviceInfo();
|
||||
}
|
||||
|
||||
snHardwareInfoReq.setModel(Build.MODEL);
|
||||
snHardwareInfoReq.setBrand(Build.BRAND);
|
||||
snHardwareInfoReq.setBoard(Build.BOARD);
|
||||
snHardwareInfoReq.setAndroidApi(Build.VERSION.SDK_INT);
|
||||
snHardwareInfoReq.setAndroidVersion(Build.VERSION.RELEASE);
|
||||
snHardwareInfoReq.setBuildId(Build.ID);
|
||||
snHardwareInfoReq.setBuildDisplayId(Build.DISPLAY);
|
||||
/**
|
||||
* 上传设备五类信息(基本 / 硬件 / 网络 / 安全 / 其他)。
|
||||
* <p>
|
||||
* 采集过程涉及文件读取与系统服务调用,耗时较长,统一放在 IO 线程执行。
|
||||
* 五个接口相互独立,使用 {@code mergeDelayError} 并发上传:
|
||||
* 任一接口失败不会中断其余接口,异常统一在最后回调,避免「一个挂了全都不传」。
|
||||
*/
|
||||
private void uploadAllDeviceInfo() {
|
||||
DeviceInfoCollector collector = new DeviceInfoCollector(mContext);
|
||||
OkHttpManager http = OkHttpManager.getInstance();
|
||||
|
||||
OkHttpManager.getInstance().getUpdateHardwareInfoObservable(snHardwareInfoReq)
|
||||
// defer + fromCallable 保证采集动作发生在订阅时的 IO 线程,而非当前线程
|
||||
Observable<BaseResponse<Void>> system = Observable
|
||||
.fromCallable(collector::collectSystemInfo)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap(http::getUpdateSystemInfoObservable);
|
||||
|
||||
Observable<BaseResponse<Void>> hardware = Observable
|
||||
.fromCallable(collector::collectHardwareInfo)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap(http::getUpdateHardwareInfoObservable);
|
||||
|
||||
Observable<BaseResponse<Void>> network = Observable
|
||||
.fromCallable(collector::collectNetworkInfo)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap(http::getUpdateNetworkInfoObservable);
|
||||
|
||||
Observable<BaseResponse<Void>> security = Observable
|
||||
.fromCallable(collector::collectSecurityInfo)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap(http::getUpdateSecurityInfoObservable);
|
||||
|
||||
Observable<BaseResponse<Void>> other = Observable
|
||||
.fromCallable(collector::collectOtherInfo)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap(http::getUpdateOtherInfoObservable);
|
||||
|
||||
Observable.mergeDelayError(
|
||||
Observable.just(system, hardware, network, security, other))
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<BaseResponse<Void>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("updateHardwareInfo", "onSubscribe: ");
|
||||
Logger.e("uploadAllDeviceInfo", "onSubscribe");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull BaseResponse<Void> voidBaseResponse) {
|
||||
Log.e("updateHardwareInfo", "onNext: " + voidBaseResponse);
|
||||
public void onNext(@NonNull BaseResponse<Void> response) {
|
||||
Logger.e("uploadAllDeviceInfo", "onNext: " + response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("updateHardwareInfo", "onError: " + e.getMessage());
|
||||
Logger.e("uploadAllDeviceInfo", "onError: ", e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("updateHardwareInfo", "onComplete: ");
|
||||
Logger.e("uploadAllDeviceInfo", "onComplete");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user