This commit is contained in:
yuqian 2025-12-23 15:51:48 +08:00
commit 22f16d7595
11 changed files with 2851 additions and 40 deletions

View File

@ -7,7 +7,7 @@ apiVersion = "82"
datastore = "1.1.7"
datastorePreferences = "1.1.7"
googleSymbolProcessingApi = "2.0.0-1.0.24"
gson = "2.13.1"
gson = "2.13.2"
jsonVersion = "20250517"
kotlin = "2.0.0"
coreKtx = "1.16.0"

View File

@ -50,6 +50,8 @@ dependencies {
implementation(libs.androidx.datastore.preferences)
// implementation("androidx.datastore:datastore-preferences-rxjava3:1.1.7")
implementation(libs.androidx.datastore)
implementation(libs.squareup.okhttp)
implementation(libs.gson)
// implementation("androidx.datastore:datastore-rxjava3:1.1.7")
implementation("androidx.security:security-state:1.0.0-alpha04")
testImplementation(libs.junit)

View File

@ -2,44 +2,517 @@ package com.xyzshell.andinfo.libs
import android.Manifest
import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.bluetooth.le.BluetoothLeScanner
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.app.ActivityCompat
/**
* 蓝牙信息工具类
* 提供蓝牙适配器信息已配对设备附近设备扫描蓝牙特性检测等功能
*/
class BluetoothInfo(private val context: Context) {
/**
* 蓝牙管理器
*/
private val bluetoothManager: BluetoothManager? = context.getSystemService(
BluetoothManager::class.java
)
/**
* 蓝牙适配器
*/
@get:SuppressLint("MissingPermission")
val bluetoothAdapter by lazy { bluetoothManager?.adapter }
/**
* 蓝牙设备信息数据类
*/
data class DeviceInfo(
val name: String?, // 设备名称
val address: String, // MAC地址
val bondState: Int, // 配对状态
val bondStateText: String, // 配对状态文本
val deviceClass: Int?, // 设备类型
val deviceType: Int?, // 设备类型LE/Classic/Dual
val rssi: Int? = null, // 信号强度(仅扫描设备)
val uuids: List<String>? = null // 支持的服务UUID
)
/**
* 蓝牙特性信息数据类
*/
data class BluetoothFeatures(
val version: String, // 蓝牙版本
val bluetoothLe: Boolean, // 是否支持BLE
val bluetooth: Boolean, // 是否支持经典蓝牙
// Bluetooth 4.x 特性
val le2MbPhy: Boolean, // 2Mbps PHY
val leCodedPhy: Boolean, // Coded PHY
val leExtendedAdvertising: Boolean, // 扩展广播
val lePeriodicAdvertising: Boolean, // 周期性广播
// Bluetooth 5.x 特性
val bluetooth5Support: Boolean, // Bluetooth 5支持
val leAudioSupport: Boolean, // LE Audio支持
val leAudioBroadcast: Boolean, // LE Audio广播
val leAudioUnicast: Boolean, // LE Audio单播
// 其他特性
val offloadedFiltering: Boolean, // 硬件过滤
val offloadedScanBatching: Boolean, // 批量扫描
val multipleAdvertisement: Boolean, // 多广播
val lowEnergyExtended: Boolean // 扩展LE
)
// ==================== 基本信息 ====================
/**
* 获取蓝牙适配器名称
* 需要权限: BLUETOOTH_CONNECT (Android 12+)
*/
@get:SuppressLint("MissingPermission")
val adapterName: String?
get() = bluetoothAdapter?.name
get() = if (checkBluetoothConnectPermission()) {
bluetoothAdapter?.name
} else null
/**
* 获取蓝牙适配器MAC地址
* 需要权限: BLUETOOTH_CONNECT (Android 12+)
* 注意: Android 6.0+ 返回固定地址 02:00:00:00:00:00
*/
@get:SuppressLint("HardwareIds", "MissingPermission")
val adapterMacAddress: String?
get() = bluetoothAdapter?.address
get() = if (checkBluetoothConnectPermission()) {
bluetoothAdapter?.address
} else null
@get:SuppressLint("MissingPermission")
val bondedDevices: List<Pair<String?, String>>
get() = bluetoothAdapter?.bondedDevices?.map {
Pair(it.name, it.address)
} ?: emptyList()
/**
* 蓝牙是否已启用
*/
val isEnabled: Boolean
get() = bluetoothAdapter?.isEnabled == true
/**
* 蓝牙适配器状态
*/
val state: Int
get() = bluetoothAdapter?.state ?: BluetoothAdapter.STATE_OFF
/**
* 蓝牙适配器状态文本
*/
val stateText: String
get() = when (state) {
BluetoothAdapter.STATE_OFF -> "关闭"
BluetoothAdapter.STATE_TURNING_ON -> "正在开启"
BluetoothAdapter.STATE_ON -> "已开启"
BluetoothAdapter.STATE_TURNING_OFF -> "正在关闭"
else -> "未知"
}
/**
* 是否支持低功耗蓝牙BLE
*/
val isBluetoothLeSupported: Boolean
get() = context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
/**
* 是否正在扫描设备
*/
@get:SuppressLint("MissingPermission")
val isDiscovering: Boolean
get() = if (checkBluetoothScanPermission()) {
bluetoothAdapter?.isDiscovering == true
} else false
// ==================== 已配对设备 ====================
/**
* 获取已配对设备列表简化版本兼容旧代码
* 需要权限: BLUETOOTH_CONNECT (Android 12+)
*/
@get:SuppressLint("MissingPermission")
val bondedDevices: List<Pair<String?, String>>
get() = if (checkBluetoothConnectPermission()) {
bluetoothAdapter?.bondedDevices?.map {
Pair(it.name, it.address)
} ?: emptyList()
} else emptyList()
/**
* 获取已配对设备详细信息列表
* 需要权限: BLUETOOTH_CONNECT (Android 12+)
*/
@SuppressLint("MissingPermission")
fun getBondedDevicesInfo(): List<DeviceInfo> {
if (!checkBluetoothConnectPermission()) return emptyList()
return try {
bluetoothAdapter?.bondedDevices?.map { device ->
DeviceInfo(
name = device.name,
address = device.address,
bondState = device.bondState,
bondStateText = getBondStateText(device.bondState),
deviceClass = device.bluetoothClass?.deviceClass,
deviceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
device.type
} else null,
uuids = device.uuids?.map { it.toString() }
)
} ?: emptyList()
} catch (e: SecurityException) {
emptyList()
}
}
// ==================== 附近设备扫描 ====================
/**
* 扫描附近的经典蓝牙设备
* 需要权限: BLUETOOTH_SCAN, ACCESS_FINE_LOCATION (Android 12+)
* @param onDeviceFound 发现设备回调
* @param onScanFinished 扫描完成回调
*/
@SuppressLint("MissingPermission")
fun scanNearbyDevices(
onDeviceFound: (DeviceInfo) -> Unit,
onScanFinished: () -> Unit
) {
if (!checkBluetoothScanPermission() || !checkLocationPermission()) {
onScanFinished()
return
}
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
BluetoothDevice.ACTION_FOUND -> {
val device: BluetoothDevice? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice::class.java)
} else {
@Suppress("DEPRECATION")
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
}
val rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE).toInt()
device?.let {
onDeviceFound(
DeviceInfo(
name = it.name,
address = it.address,
bondState = it.bondState,
bondStateText = getBondStateText(it.bondState),
deviceClass = it.bluetoothClass?.deviceClass,
deviceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
it.type
} else null,
rssi = rssi,
uuids = it.uuids?.map { uuid -> uuid.toString() }
)
)
}
}
BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
context.unregisterReceiver(this)
onScanFinished()
}
}
}
}
val filter = IntentFilter().apply {
addAction(BluetoothDevice.ACTION_FOUND)
addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
}
context.registerReceiver(receiver, filter)
bluetoothAdapter?.startDiscovery()
}
/**
* 扫描附近的BLE设备
* 需要权限: BLUETOOTH_SCAN, ACCESS_FINE_LOCATION (Android 12+)
* @param onDeviceFound 发现设备回调
* @param durationMillis 扫描时长毫秒
*/
@SuppressLint("MissingPermission")
fun scanLeDevices(
onDeviceFound: (DeviceInfo) -> Unit,
durationMillis: Long = 10000
) {
if (!checkBluetoothScanPermission() || !checkLocationPermission()) {
return
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return
val leScanner: BluetoothLeScanner? = bluetoothAdapter?.bluetoothLeScanner
val scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
val device = result.device
onDeviceFound(
DeviceInfo(
name = device.name,
address = device.address,
bondState = device.bondState,
bondStateText = getBondStateText(device.bondState),
deviceClass = device.bluetoothClass?.deviceClass,
deviceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
device.type
} else null,
rssi = result.rssi,
uuids = result.scanRecord?.serviceUuids?.map { it.toString() }
)
)
}
}
leScanner?.startScan(scanCallback)
// 定时停止扫描
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
leScanner?.stopScan(scanCallback)
}, durationMillis)
}
/**
* 停止设备扫描
*/
@SuppressLint("MissingPermission")
fun stopScan() {
if (checkBluetoothScanPermission()) {
bluetoothAdapter?.cancelDiscovery()
}
}
// ==================== 蓝牙特性检测 ====================
/**
* 获取蓝牙特性信息
*/
fun getBluetoothFeatures(): BluetoothFeatures {
val adapter = bluetoothAdapter
return BluetoothFeatures(
version = getBluetoothVersion(),
bluetoothLe = isBluetoothLeSupported,
bluetooth = context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH),
// Bluetooth 4.x 特性
le2MbPhy = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
adapter?.isLe2MPhySupported == true
} else false,
leCodedPhy = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
adapter?.isLeCodedPhySupported == true
} else false,
leExtendedAdvertising = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
adapter?.isLeExtendedAdvertisingSupported == true
} else false,
lePeriodicAdvertising = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
adapter?.isLePeriodicAdvertisingSupported == true
} else false,
// Bluetooth 5.x 特性
bluetooth5Support = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
adapter?.isLe2MPhySupported == true ||
adapter?.isLeCodedPhySupported == true ||
adapter?.isLeExtendedAdvertisingSupported == true
} else false,
leAudioSupport = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.packageManager.hasSystemFeature("android.hardware.bluetooth.le.audio")
} else false,
leAudioBroadcast = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.packageManager.hasSystemFeature("android.hardware.bluetooth.le.audio.broadcast_source")
} else false,
leAudioUnicast = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.packageManager.hasSystemFeature("android.hardware.bluetooth.le.audio.unicast_client")
} else false,
// 其他特性
offloadedFiltering = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
adapter?.isOffloadedFilteringSupported == true
} else false,
offloadedScanBatching = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
adapter?.isOffloadedScanBatchingSupported == true
} else false,
multipleAdvertisement = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
adapter?.isMultipleAdvertisementSupported == true
} else false,
lowEnergyExtended = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
adapter?.isLeExtendedAdvertisingSupported == true
} else false
)
}
/**
* 获取蓝牙版本根据支持的特性推断
*/
private fun getBluetoothVersion(): String {
val adapter = bluetoothAdapter ?: return "未知"
return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
context.packageManager.hasSystemFeature("android.hardware.bluetooth.le.audio") ->
"Bluetooth 5.2+"
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
(adapter.isLe2MPhySupported || adapter.isLeCodedPhySupported) ->
"Bluetooth 5.0"
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isBluetoothLeSupported ->
"Bluetooth 4.2"
isBluetoothLeSupported ->
"Bluetooth 4.0"
else ->
"Bluetooth 3.0 或更低"
}
}
// ==================== 权限检查 ====================
/**
* 所需权限列表
*/
val requiredPermissions: Array<String>
get() = buildList {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
add(Manifest.permission.BLUETOOTH_CONNECT)
add(Manifest.permission.BLUETOOTH_SCAN)
} else {
add(Manifest.permission.BLUETOOTH)
add(Manifest.permission.BLUETOOTH_ADMIN)
}
add(Manifest.permission.ACCESS_FINE_LOCATION)
}.toTypedArray()
/**
* 检查蓝牙连接权限
*/
private fun checkBluetoothConnectPermission(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
ActivityCompat.checkSelfPermission(
context,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
} else {
ActivityCompat.checkSelfPermission(
context,
Manifest.permission.BLUETOOTH
) == PackageManager.PERMISSION_GRANTED
}
}
/**
* 检查蓝牙扫描权限
*/
private fun checkBluetoothScanPermission(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
ActivityCompat.checkSelfPermission(
context,
Manifest.permission.BLUETOOTH_SCAN
) == PackageManager.PERMISSION_GRANTED
} else {
ActivityCompat.checkSelfPermission(
context,
Manifest.permission.BLUETOOTH_ADMIN
) == PackageManager.PERMISSION_GRANTED
}
}
/**
* 检查位置权限
*/
private fun checkLocationPermission(): Boolean {
return ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
// ==================== 辅助方法 ====================
/**
* 获取配对状态文本
*/
private fun getBondStateText(state: Int): String = when (state) {
BluetoothDevice.BOND_NONE -> "未配对"
BluetoothDevice.BOND_BONDING -> "正在配对"
BluetoothDevice.BOND_BONDED -> "已配对"
else -> "未知"
}
/**
* 获取设备类型文本
*/
fun getDeviceTypeText(type: Int?): String = when (type) {
BluetoothDevice.DEVICE_TYPE_CLASSIC -> "经典蓝牙"
BluetoothDevice.DEVICE_TYPE_LE -> "低功耗蓝牙"
BluetoothDevice.DEVICE_TYPE_DUAL -> "双模蓝牙"
else -> "未知"
}
/**
* 获取文本格式的蓝牙信息摘要
*/
fun text(): String {
val sb = StringBuilder()
// 基本信息
sb.append("=== 蓝牙基本信息 ===\n")
sb.append("适配器名称: ${adapterName ?: "未知"}\n")
sb.append("MAC地址: ${adapterMacAddress ?: "未知"}\n")
sb.append("状态: $stateText\n")
sb.append("是否启用: ${if (isEnabled) "是" else "否"}\n")
// 蓝牙特性
val features = getBluetoothFeatures()
sb.append("\n=== 蓝牙特性 ===\n")
sb.append("蓝牙版本: ${features.version}\n")
sb.append("经典蓝牙: ${if (features.bluetooth) "支持" else "不支持"}\n")
sb.append("低功耗蓝牙: ${if (features.bluetoothLe) "支持" else "不支持"}\n")
sb.append("Bluetooth 5: ${if (features.bluetooth5Support) "支持" else "不支持"}\n")
sb.append("LE Audio: ${if (features.leAudioSupport) "支持" else "不支持"}\n")
sb.append("2Mbps PHY: ${if (features.le2MbPhy) "支持" else "不支持"}\n")
sb.append("Coded PHY: ${if (features.leCodedPhy) "支持" else "不支持"}\n")
sb.append("扩展广播: ${if (features.leExtendedAdvertising) "支持" else "不支持"}\n")
sb.append("周期性广播: ${if (features.lePeriodicAdvertising) "支持" else "不支持"}\n")
// 已配对设备
val bonded = getBondedDevicesInfo()
sb.append("\n=== 已配对设备 (${bonded.size}) ===\n")
bonded.forEach { device ->
sb.append("设备: ${device.name ?: "未知"}\n")
sb.append(" 地址: ${device.address}\n")
sb.append(" 状态: ${device.bondStateText}\n")
device.deviceType?.let {
sb.append(" 类型: ${getDeviceTypeText(it)}\n")
}
}
return sb.toString()
}
}

View File

@ -5,101 +5,257 @@ import android.os.Build
// import androidx.security.state.SecurityStateManagerCompat
import java.util.Date
/**
* 系统构建信息工具类
* 提供 Android 系统构建版本编译信息安全补丁JVM 信息等
*/
class BuildInfo(private val context: Context) {
// private val securityStateManager = SecurityStateManagerCompat(context)
// private val globalSecurityState = securityStateManager.getGlobalSecurityState()
// ==================== 构建基本信息 ====================
/**
* 系统构建指纹
* 格式: brand/product/device:version/id/incremental:type/tags
* 示例: google/redfin/redfin:13/TP1A.220905.004/9012412:user/release-keys
*/
val fingerprint: String
get() = Build.FINGERPRINT
/**
* 构建标签
* 通常为 "release-keys" (正式版) "test-keys" (测试版)
*/
val tags: String
get() = Build.TAGS
/**
* 构建类型
* 通常为 "user" (用户版)"userdebug" (调试版) "eng" (工程版)
*/
val type: String
get() = Build.TYPE
/**
* 系统构建日期
* 返回构建时的时间戳
*/
val buildDate: Date
get() = Date(Build.TIME)
/**
* 构建主机名
* 编译系统的主机名
*/
val host: String
get() = Build.HOST
/**
* 构建用户
* 执行编译的用户名
*/
val user: String
get() = Build.USER
/**
* 构建 ID
* 构建的唯一标识符通常是版本号
* 示例: TP1A.220905.004
*/
val id: String
get() = Build.ID
/**
* 显示 ID
* 用于向用户显示的构建标识符
*/
val display: String
get() = Build.DISPLAY
// ==================== Android 版本信息 ====================
/**
* Android 版本号
* 示例: "13", "12", "11"
*/
val versionRelease: String
get() = Build.VERSION.RELEASE
/**
* 版本代号
* 开发阶段的代号正式版通常为 "REL"
*/
val versionCodename: String
get() = Build.VERSION.CODENAME
/**
* SDK 版本号API Level
* 示例: 33 (Android 13), 31 (Android 12), 30 (Android 11)
*/
val versionSdkInt: Int
get() = Build.VERSION.SDK_INT
/**
* 预览版 SDK 版本号
* 如果是预览版返回非 0 正式版返回 0
*/
val versionPreviewSdkInt: Int
get() = Build.VERSION.PREVIEW_SDK_INT
/**
* 安全补丁级别
* 格式: YYYY-MM-DD
* 示例: "2023-12-05"
*/
val securityPatch: String
get() = Build.VERSION.SECURITY_PATCH
/**
* 基础操作系统
* 通常为空除非是基于其他版本构建
*/
val baseOs: String
get() = Build.VERSION.BASE_OS
/**
* 增量版本
* 表示在同一版本号下的递增更新
*/
val incremental: String
get() = Build.VERSION.INCREMENTAL
/**
* 版本号或代号
* Android 11+ 可用
* 如果是正式版返回版本号预览版返回代号
*/
val releaseOrCodename: String?
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) Build.VERSION.RELEASE_OR_CODENAME else null
/**
* 媒体性能等级
* Android 12+ 可用
* 返回设备的媒体性能等级0 表示未定义
* 示例: 31 (Android 12), 33 (Android 13)
*/
val mediaPerformanceClass: Int
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) Build.VERSION.MEDIA_PERFORMANCE_CLASS else 0
/**
* 版本显示字符串
* Android 13+ 可用
* 用于显示的完整版本号包括预览版
*/
val releaseOrPreviewDisplay: String?
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY else null
// ==================== JVM 信息 ====================
/**
* JVM 名称
* 示例: "Dalvik" (Android) "Java HotSpot(TM) 64-Bit Server VM"
*/
val jvmName: String?
get() = System.getProperty("java.vm.name")
/**
* JVM 供应商
* 示例: "The Android Project" "Oracle Corporation"
*/
val jvmVendor: String?
get() = System.getProperty("java.vm.vendor")
/**
* JVM 版本
* 示例: "2.1.0"
*/
val jvmVersion: String?
get() = System.getProperty("java.vm.version")
/**
* Java 类版本
* 示例: "50.0" (Java 6), "52.0" (Java 8)
*/
val jvmClassVersion: String?
get() = System.getProperty("java.class.version")
/**
* Java 规范名称
* 示例: "Dalvik Core Library"
*/
val jvmSpecificationName: String?
get() = System.getProperty("java.specification.name")
/**
* Java 规范供应商
* 示例: "The Android Project"
*/
val jvmSpecificationVendor: String?
get() = System.getProperty("java.specification.vendor")
/**
* Java 规范版本
* 示例: "0.9"
*/
val jvmSpecificationVersion: String?
get() = System.getProperty("java.specification.version")
// ==================== 安全与内核信息 ====================
/**
* 厂商安全补丁级别
* 需要 SecurityStateManagerCompat 支持
* 某些设备可能有额外的厂商安全补丁
*/
val vendorSecurityPatchLevel: String?
get() = null // globalSecurityState.getString(SecurityStateManagerCompat.KEY_VENDOR_SPL)
/**
* 内核版本
* 需要 SecurityStateManagerCompat 支持
* 示例: "5.10.157"
*/
val kernelVersion: String?
get() = null // globalSecurityState.getString(SecurityStateManagerCompat.KEY_KERNEL_VERSION)
/**
* 完整内核版本
* 包含内核版本和编译信息
* 示例: "5.10.157-android13-4-00001-gf0123456789a-ab9876543"
*/
val kernelCompleteVersion: String?
get() = System.getProperty("os.version")
/**
* Bootloader 版本
* 设备的引导加载程序版本
*/
val bootloaderVersion: String
get() = Build.BOOTLOADER
/**
* 基带版本无线电版本
* 移动网络基带固件版本
*/
val radioVersion: String?
get() = Build.getRadioVersion()
// ==================== 分区信息 ====================
/**
* 指纹分区信息列表
* Android 10+ 可用
* 包含系统各分区的名称和指纹信息
*
* 常见分区
* - system: 系统分区
* - vendor: 厂商分区
* - product: 产品分区
* - odm: ODM原始设计制造商分区
* - system_ext: 系统扩展分区
*/
val fingerprintedPartitions: List<PartitionInfo>
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Build.getFingerprintedPartitions().map { PartitionInfo(it.name, it.fingerprint) }
@ -107,5 +263,72 @@ class BuildInfo(private val context: Context) {
emptyList()
}
/**
* 分区信息数据类
* @property name 分区名称
* @property fingerprint 分区指纹构建标识
*/
data class PartitionInfo(val name: String, val fingerprint: String)
// ==================== 辅助方法 ====================
/**
* 获取文本格式的构建信息摘要
* @return 格式化的文本信息
*/
fun text(): String {
val sb = StringBuilder()
// 基本构建信息
sb.append("=== 系统构建信息 ===\n")
sb.append("指纹: $fingerprint\n")
sb.append("显示ID: $display\n")
sb.append("构建ID: $id\n")
sb.append("类型: $type\n")
sb.append("标签: $tags\n")
sb.append("构建日期: $buildDate\n")
sb.append("构建用户: $user\n")
sb.append("构建主机: $host\n")
// Android 版本信息
sb.append("\n=== Android 版本 ===\n")
sb.append("版本号: $versionRelease\n")
sb.append("SDK版本: $versionSdkInt\n")
sb.append("代号: $versionCodename\n")
sb.append("增量版本: $incremental\n")
sb.append("安全补丁: $securityPatch\n")
releaseOrCodename?.let { sb.append("版本/代号: $it\n") }
if (mediaPerformanceClass > 0) {
sb.append("媒体性能等级: $mediaPerformanceClass\n")
}
releaseOrPreviewDisplay?.let { sb.append("版本显示: $it\n") }
if (baseOs.isNotEmpty()) {
sb.append("基础OS: $baseOs\n")
}
// JVM 信息
sb.append("\n=== JVM 信息 ===\n")
jvmName?.let { sb.append("JVM名称: $it\n") }
jvmVendor?.let { sb.append("JVM供应商: $it\n") }
jvmVersion?.let { sb.append("JVM版本: $it\n") }
jvmClassVersion?.let { sb.append("类版本: $it\n") }
jvmSpecificationName?.let { sb.append("规范名称: $it\n") }
jvmSpecificationVersion?.let { sb.append("规范版本: $it\n") }
// 硬件信息
sb.append("\n=== 硬件信息 ===\n")
sb.append("Bootloader: $bootloaderVersion\n")
radioVersion?.let { sb.append("基带版本: $it\n") }
kernelCompleteVersion?.let { sb.append("内核版本: $it\n") }
// 分区信息
if (fingerprintedPartitions.isNotEmpty()) {
sb.append("\n=== 分区指纹 ===\n")
fingerprintedPartitions.forEach { partition ->
sb.append("${partition.name}: ${partition.fingerprint}\n")
}
}
return sb.toString()
}
}

View File

@ -8,25 +8,111 @@ import android.hardware.camera2.CameraMetadata
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Range
import android.util.SizeF
import com.xyzshell.andinfo.R
import com.xyzshell.andinfo.libs.camera.models.CameraCapability
import com.xyzshell.andinfo.libs.camera.models.CameraFacing
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import kotlin.math.sqrt
/**
* 摄像头信息工具类
* 提供摄像头硬件参数特性功能列表等详细信息
* 需要权限: CAMERA
*/
class CameraInfo(private val context: Context) {
/**
* 摄像头管理器
*/
private val cameraManager: CameraManager? = context.getSystemService(
CameraManager::class.java
)
/**
* 摄像头详细信息数据类
*/
data class CameraDetails(
val cameraId: String, // 摄像头ID
val facing: CameraFacing?, // 前置/后置/外置
val facingText: String, // 方向文本
// 传感器信息
val megaPixels: Double?, // 有效百万像素
val resolution: String?, // 分辨率宽x高
val sensorSize: String?, // 传感器尺寸mm
val sensorSizeInch: Double?, // 传感器英寸
val pixelSize: Double?, // 单像素尺寸μm
val colorFilterArrangement: String?, // 滤镜颜色排列Bayer等
// 光学参数
val focalLength: FloatArray?, // 焦距mm
val apertures: FloatArray?, // 光圈值
val fieldOfView: String?, // 视野角度
val focal35mmEquivalent: Double?, // 35mm等效焦距
val cropFactor: Double?, // 裁切系数
// 曝光参数
val isoRange: Range<Int>?, // ISO感光范围
val exposureTimeRange: Range<Long>?, // 曝光时间范围(纳秒)
val shutterSpeedRange: String?, // 快门速度范围
val exposureCompensationRange: String?, // 曝光补偿范围
val exposureCompensationStep: Double?, // 曝光补偿步长
// 对焦参数
val minimumFocusDistance: Float?, // 最小对焦距离(屈光度)
val minimumFocusDistanceCm: Double?, // 最小对焦距离(厘米)
val hyperFocalDistance: Float?, // 超焦距
// 功能特性
val hasFlash: Boolean?, // 是否有闪光灯
val hasOpticalStabilization: Boolean?, // 光学防抖OIS
val hasVideoStabilization: Boolean?, // 视频防抖
val hasAutoFocus: Boolean?, // 自动对焦
val camera2ApiSupport: String?, // Camera2 API支持级别
// 面部检测
val maxFaceCount: Int?, // 最大面部检测数量
val faceDetectionModes: List<String>?, // 面部检测模式列表
// 功能列表
val capabilities: List<CameraCapability>, // 摄像头能力
val supportedFeatures: List<String>, // 支持的功能HDR、RAW等
// 模式列表
val autoExposureModes: List<String>?, // 自动曝光模式
val autoFocusModes: List<String>?, // 自动对焦模式
val whiteBalanceModes: List<String>?, // 白平衡模式
val sceneModes: List<String>?, // 场景模式
val colorEffects: List<String>?, // 色彩效果
// 视频能力
val maxFrameRate: Int?, // 最大帧率
val supportsHighSpeedVideo: Boolean, // 是否支持高速视频
val supports4K: Boolean, // 是否支持4K录制
val supports8K: Boolean // 是否支持8K录制
)
/**
* 获取所有摄像头ID列表
*/
val cameraIds: List<String>
get() = cameraManager?.cameraIdList?.toList() ?: emptyList()
/**
* 获取指定摄像头的特性对象
* @param cameraId 摄像头ID
* @return CameraCharacteristics 对象
*/
fun getCameraCharacteristics(cameraId: String): CameraCharacteristics? {
return cameraManager?.getCameraCharacteristics(cameraId)
}
/**
* 监听摄像头可用性变化的 Flow
*/
fun cameraIdsFlow() = cameraManager?.let { manager ->
callbackFlow {
val onCameraIdUpdated = {
@ -55,6 +141,12 @@ class CameraInfo(private val context: Context) {
}
}
// ==================== 基本信息 ====================
/**
* 获取摄像头朝向
* @return 前置/后置/外置
*/
fun getCameraFacing(characteristics: CameraCharacteristics): CameraFacing? {
return characteristics.get(CameraCharacteristics.LENS_FACING)?.let {
when (it) {
@ -66,22 +158,291 @@ class CameraInfo(private val context: Context) {
}
}
/**
* 获取摄像头朝向文本
*/
fun getCameraFacingText(characteristics: CameraCharacteristics): String {
return when (getCameraFacing(characteristics)) {
CameraFacing.BACK -> "后置"
CameraFacing.FRONT -> "前置"
CameraFacing.EXTERNAL -> "外置"
else -> "未知"
}
}
// ==================== 传感器信息 ====================
/**
* 获取像素阵列尺寸分辨率
* @return 格式: "宽x高"
*/
fun getPixelArraySize(characteristics: CameraCharacteristics): String? {
return characteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)?.toString()
}
/**
* 获取有效百万像素
*/
fun getMegaPixels(characteristics: CameraCharacteristics): Double? {
val size = characteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)
return size?.let { (it.width * it.height) / 1_000_000.0 }
}
/**
* 获取传感器物理尺寸
* @return 格式: "宽x高 mm"
*/
fun getSensorPhysicalSize(characteristics: CameraCharacteristics): String? {
return characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)?.toString()
return characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)?.let {
String.format("%.2f x %.2f mm", it.width, it.height)
}
}
fun hasFlashUnit(characteristics: CameraCharacteristics): Boolean? {
return characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)
/**
* 获取传感器尺寸英寸对角线
*/
fun getSensorSizeInch(characteristics: CameraCharacteristics): Double? {
return characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)?.let {
val diagonal = sqrt((it.width * it.width + it.height * it.height).toDouble())
diagonal / 25.4 // 转换为英寸
}
}
/**
* 获取单像素尺寸微米
*/
fun getPixelSize(characteristics: CameraCharacteristics): Double? {
val physicalSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)
val pixelArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)
return if (physicalSize != null && pixelArray != null) {
val pixelWidth = (physicalSize.width * 1000) / pixelArray.width
val pixelHeight = (physicalSize.height * 1000) / pixelArray.height
(pixelWidth + pixelHeight) / 2.0
} else null
}
/**
* 获取滤镜颜色排列
*/
fun getColorFilterArrangement(characteristics: CameraCharacteristics): String? {
return characteristics.get(CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT)?.let {
when (it) {
CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB -> "RGGB (Bayer)"
CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG -> "GRBG (Bayer)"
CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG -> "GBRG (Bayer)"
CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR -> "BGGR (Bayer)"
CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB -> "RGB"
5 -> "Mono/NIR (单色/近红外)" // MONO/NIR
else -> "未知"
}
}
}
// ==================== 光学参数 ====================
/**
* 获取焦距数组
*/
fun getFocalLength(characteristics: CameraCharacteristics): FloatArray? {
return characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)
}
/**
* 获取光圈值数组
*/
fun getAvailableApertures(characteristics: CameraCharacteristics): FloatArray? {
return characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES)
}
/**
* 获取35mm等效焦距
*/
fun getFocal35mmEquivalent(characteristics: CameraCharacteristics): Double? {
val focalLength = getFocalLength(characteristics)?.firstOrNull()
val cropFactor = getCropFactor(characteristics)
return if (focalLength != null && cropFactor != null) {
focalLength * cropFactor
} else null
}
/**
* 获取裁切系数相对于全画幅
*/
fun getCropFactor(characteristics: CameraCharacteristics): Double? {
val sensorSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)
return sensorSize?.let {
val diagonal = sqrt((it.width * it.width + it.height * it.height).toDouble())
43.27 / diagonal // 全画幅对角线约43.27mm
}
}
/**
* 获取视野角度
*/
fun getFieldOfView(characteristics: CameraCharacteristics): String? {
val focalLength = getFocalLength(characteristics)?.firstOrNull()
val sensorSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)
return if (focalLength != null && sensorSize != null) {
val hFov = 2 * Math.toDegrees(Math.atan((sensorSize.width / 2.0 / focalLength).toDouble()))
val vFov = 2 * Math.toDegrees(Math.atan((sensorSize.height / 2.0 / focalLength).toDouble()))
String.format("H: %.1f°, V: %.1f°", hFov, vFov)
} else null
}
// ==================== 曝光参数 ====================
/**
* 获取ISO感光度范围
*/
fun getIsoRange(characteristics: CameraCharacteristics): Range<Int>? {
return characteristics.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE)
}
/**
* 获取曝光时间范围纳秒
*/
fun getExposureTimeRange(characteristics: CameraCharacteristics): Range<Long>? {
return characteristics.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE)
}
/**
* 获取快门速度范围格式化字符串
*/
fun getShutterSpeedRange(characteristics: CameraCharacteristics): String? {
val exposureRange = getExposureTimeRange(characteristics)
return exposureRange?.let {
val minSeconds = it.lower / 1_000_000_000.0
val maxSeconds = it.upper / 1_000_000_000.0
String.format("1/%.0f - %.2fs", 1.0 / minSeconds, maxSeconds)
}
}
/**
* 获取曝光补偿范围
*/
fun getExposureCompensationRange(characteristics: CameraCharacteristics): String? {
val range = characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE)
val step = characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP)
return if (range != null && step != null) {
val minEv = range.lower * step.toDouble()
val maxEv = range.upper * step.toDouble()
String.format("%.1f ~ %.1f EV", minEv, maxEv)
} else null
}
/**
* 获取曝光补偿步长
*/
fun getExposureCompensationStep(characteristics: CameraCharacteristics): Double? {
return characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP)?.toDouble()
}
// ==================== 对焦参数 ====================
/**
* 获取最小对焦距离屈光度
*/
fun getMinimumFocusDistance(characteristics: CameraCharacteristics): Float? {
return characteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE)
}
/**
* 获取最小对焦距离厘米
*/
fun getMinimumFocusDistanceCm(characteristics: CameraCharacteristics): Double? {
val diopters = getMinimumFocusDistance(characteristics)
return diopters?.let {
if (it > 0) 100.0 / it else null
}
}
/**
* 获取超焦距
*/
fun getHyperFocalDistance(characteristics: CameraCharacteristics): Float? {
return characteristics.get(CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE)
}
// ==================== 功能特性 ====================
/**
* 是否有闪光灯
*/
fun hasFlashUnit(characteristics: CameraCharacteristics): Boolean? {
return characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)
}
/**
* 是否支持光学防抖OIS
*/
fun hasOpticalStabilization(characteristics: CameraCharacteristics): Boolean {
val modes = characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION)
return modes?.contains(CameraCharacteristics.LENS_OPTICAL_STABILIZATION_MODE_ON) == true
}
/**
* 是否支持视频防抖
*/
fun hasVideoStabilization(characteristics: CameraCharacteristics): Boolean {
val modes = characteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES)
return modes?.contains(CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_ON) == true
}
/**
* 是否支持自动对焦
*/
fun hasAutoFocus(characteristics: CameraCharacteristics): Boolean {
val minDistance = getMinimumFocusDistance(characteristics)
return minDistance != null && minDistance > 0
}
/**
* 获取Camera2 API支持级别
*/
fun getCamera2ApiSupport(characteristics: CameraCharacteristics): String? {
return characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)?.let {
when (it) {
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED -> "LIMITED"
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL -> "FULL"
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY -> "LEGACY"
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3 -> "LEVEL_3"
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) "EXTERNAL" else "未知"
else -> "未知"
}
}
}
// ==================== 面部检测 ====================
/**
* 获取最大面部检测数量
*/
fun getMaxFaceCount(characteristics: CameraCharacteristics): Int? {
return characteristics.get(CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT)
}
/**
* 获取面部检测模式列表
*/
fun getFaceDetectionModes(characteristics: CameraCharacteristics): List<String>? {
return characteristics.get(CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES)?.map {
when (it) {
CameraCharacteristics.STATISTICS_FACE_DETECT_MODE_OFF -> "关闭"
CameraCharacteristics.STATISTICS_FACE_DETECT_MODE_SIMPLE -> "简单"
CameraCharacteristics.STATISTICS_FACE_DETECT_MODE_FULL -> "完整"
else -> "未知"
}
}
}
// ==================== 能力列表 ====================
/**
* 获取摄像头能力列表
*/
fun getAvailableCapabilities(characteristics: CameraCharacteristics): List<CameraCapability> {
val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
return capabilities?.toList()?.mapNotNull { capability ->
@ -111,4 +472,235 @@ class CameraInfo(private val context: Context) {
}
} ?: emptyList()
}
/**
* 获取支持的功能列表人类可读
*/
fun getSupportedFeatures(characteristics: CameraCharacteristics): List<String> {
val features = mutableListOf<String>()
val capabilities = getAvailableCapabilities(characteristics)
if (capabilities.contains(CameraCapability.RAW)) features.add("RAW拍摄")
if (capabilities.contains(CameraCapability.BURST_CAPTURE)) features.add("连拍")
if (capabilities.contains(CameraCapability.DEPTH_OUTPUT)) features.add("景深输出")
if (capabilities.contains(CameraCapability.CONSTRAINED_HIGH_SPEED_VIDEO)) features.add("高速视频")
if (capabilities.contains(CameraCapability.MANUAL_SENSOR)) features.add("手动模式")
if (capabilities.contains(CameraCapability.MONOCHROME)) features.add("黑白模式")
if (capabilities.contains(CameraCapability.ULTRA_HIGH_RESOLUTION_SENSOR)) features.add("超高分辨率")
if (capabilities.contains(CameraCapability.DYNAMIC_RANGE_TEN_BIT)) features.add("10-bit HDR")
if (hasOpticalStabilization(characteristics)) features.add("光学防抖")
if (hasVideoStabilization(characteristics)) features.add("视频防抖")
return features
}
// ==================== 模式列表 ====================
/**
* 获取自动曝光模式列表
*/
fun getAutoExposureModes(characteristics: CameraCharacteristics): List<String>? {
return characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES)?.map {
when (it) {
CameraCharacteristics.CONTROL_AE_MODE_OFF -> "关闭"
CameraCharacteristics.CONTROL_AE_MODE_ON -> "自动"
CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH -> "自动闪光"
CameraCharacteristics.CONTROL_AE_MODE_ON_ALWAYS_FLASH -> "强制闪光"
CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE -> "防红眼"
CameraCharacteristics.CONTROL_AE_MODE_ON_EXTERNAL_FLASH -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) "外置闪光" else "未知"
else -> "未知"
}
}
}
/**
* 获取自动对焦模式列表
*/
fun getAutoFocusModes(characteristics: CameraCharacteristics): List<String>? {
return characteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES)?.map {
when (it) {
CameraCharacteristics.CONTROL_AF_MODE_OFF -> "关闭"
CameraCharacteristics.CONTROL_AF_MODE_AUTO -> "自动"
CameraCharacteristics.CONTROL_AF_MODE_MACRO -> "微距"
CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_VIDEO -> "连续视频"
CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_PICTURE -> "连续拍照"
CameraCharacteristics.CONTROL_AF_MODE_EDOF -> "EDOF"
else -> "未知"
}
}
}
/**
* 获取白平衡模式列表
*/
fun getWhiteBalanceModes(characteristics: CameraCharacteristics): List<String>? {
return characteristics.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES)?.map {
when (it) {
CameraCharacteristics.CONTROL_AWB_MODE_OFF -> "关闭"
CameraCharacteristics.CONTROL_AWB_MODE_AUTO -> "自动"
CameraCharacteristics.CONTROL_AWB_MODE_INCANDESCENT -> "白炽灯"
CameraCharacteristics.CONTROL_AWB_MODE_FLUORESCENT -> "荧光灯"
CameraCharacteristics.CONTROL_AWB_MODE_WARM_FLUORESCENT -> "暖荧光"
CameraCharacteristics.CONTROL_AWB_MODE_DAYLIGHT -> "日光"
CameraCharacteristics.CONTROL_AWB_MODE_CLOUDY_DAYLIGHT -> "阴天"
CameraCharacteristics.CONTROL_AWB_MODE_TWILIGHT -> "黄昏"
CameraCharacteristics.CONTROL_AWB_MODE_SHADE -> "阴影"
else -> "未知"
}
}
}
/**
* 获取场景模式列表
*/
fun getSceneModes(characteristics: CameraCharacteristics): List<String>? {
return characteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES)?.map {
when (it) {
CameraCharacteristics.CONTROL_SCENE_MODE_DISABLED -> "禁用"
CameraCharacteristics.CONTROL_SCENE_MODE_FACE_PRIORITY -> "人像优先"
CameraCharacteristics.CONTROL_SCENE_MODE_ACTION -> "运动"
CameraCharacteristics.CONTROL_SCENE_MODE_PORTRAIT -> "人像"
CameraCharacteristics.CONTROL_SCENE_MODE_LANDSCAPE -> "风景"
CameraCharacteristics.CONTROL_SCENE_MODE_NIGHT -> "夜景"
CameraCharacteristics.CONTROL_SCENE_MODE_NIGHT_PORTRAIT -> "夜景人像"
CameraCharacteristics.CONTROL_SCENE_MODE_THEATRE -> "剧院"
CameraCharacteristics.CONTROL_SCENE_MODE_BEACH -> "海滩"
CameraCharacteristics.CONTROL_SCENE_MODE_SNOW -> "雪景"
CameraCharacteristics.CONTROL_SCENE_MODE_SUNSET -> "日落"
CameraCharacteristics.CONTROL_SCENE_MODE_STEADYPHOTO -> "防抖"
CameraCharacteristics.CONTROL_SCENE_MODE_FIREWORKS -> "烟火"
CameraCharacteristics.CONTROL_SCENE_MODE_SPORTS -> "体育"
CameraCharacteristics.CONTROL_SCENE_MODE_PARTY -> "派对"
CameraCharacteristics.CONTROL_SCENE_MODE_CANDLELIGHT -> "烛光"
CameraCharacteristics.CONTROL_SCENE_MODE_BARCODE -> "条形码"
CameraCharacteristics.CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) "高速视频" else "未知"
CameraCharacteristics.CONTROL_SCENE_MODE_HDR -> "HDR"
else -> "未知"
}
}
}
/**
* 获取色彩效果列表
*/
fun getColorEffects(characteristics: CameraCharacteristics): List<String>? {
return characteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS)?.map {
when (it) {
CameraCharacteristics.CONTROL_EFFECT_MODE_OFF -> "关闭"
CameraCharacteristics.CONTROL_EFFECT_MODE_MONO -> "黑白"
CameraCharacteristics.CONTROL_EFFECT_MODE_NEGATIVE -> "负片"
CameraCharacteristics.CONTROL_EFFECT_MODE_SOLARIZE -> "曝光"
CameraCharacteristics.CONTROL_EFFECT_MODE_SEPIA -> "棕褐色"
CameraCharacteristics.CONTROL_EFFECT_MODE_POSTERIZE -> "海报化"
CameraCharacteristics.CONTROL_EFFECT_MODE_WHITEBOARD -> "白板"
CameraCharacteristics.CONTROL_EFFECT_MODE_BLACKBOARD -> "黑板"
CameraCharacteristics.CONTROL_EFFECT_MODE_AQUA -> "水蓝"
else -> "未知"
}
}
}
// ==================== 视频能力 ====================
/**
* 获取最大帧率
*/
fun getMaxFrameRate(characteristics: CameraCharacteristics): Int? {
val ranges = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES)
return ranges?.maxOfOrNull { it.upper }
}
/**
* 是否支持高速视频
*/
fun supportsHighSpeedVideo(characteristics: CameraCharacteristics): Boolean {
return getAvailableCapabilities(characteristics)
.contains(CameraCapability.CONSTRAINED_HIGH_SPEED_VIDEO)
}
/**
* 是否支持4K录制
*/
fun supports4K(characteristics: CameraCharacteristics): Boolean {
// 简化判断,实际需要检查 StreamConfigurationMap
val resolution = getPixelArraySize(characteristics)
return resolution?.contains("3840") == true || resolution?.contains("4096") == true
}
/**
* 是否支持8K录制
*/
fun supports8K(characteristics: CameraCharacteristics): Boolean {
val resolution = getPixelArraySize(characteristics)
return resolution?.contains("7680") == true
}
// ==================== 综合信息 ====================
/**
* 获取摄像头详细信息
*/
fun getCameraDetails(cameraId: String): CameraDetails? {
val characteristics = getCameraCharacteristics(cameraId) ?: return null
return CameraDetails(
cameraId = cameraId,
facing = getCameraFacing(characteristics),
facingText = getCameraFacingText(characteristics),
megaPixels = getMegaPixels(characteristics),
resolution = getPixelArraySize(characteristics),
sensorSize = getSensorPhysicalSize(characteristics),
sensorSizeInch = getSensorSizeInch(characteristics),
pixelSize = getPixelSize(characteristics),
colorFilterArrangement = getColorFilterArrangement(characteristics),
focalLength = getFocalLength(characteristics),
apertures = getAvailableApertures(characteristics),
fieldOfView = getFieldOfView(characteristics),
focal35mmEquivalent = getFocal35mmEquivalent(characteristics),
cropFactor = getCropFactor(characteristics),
isoRange = getIsoRange(characteristics),
exposureTimeRange = getExposureTimeRange(characteristics),
shutterSpeedRange = getShutterSpeedRange(characteristics),
exposureCompensationRange = getExposureCompensationRange(characteristics),
exposureCompensationStep = getExposureCompensationStep(characteristics),
minimumFocusDistance = getMinimumFocusDistance(characteristics),
minimumFocusDistanceCm = getMinimumFocusDistanceCm(characteristics),
hyperFocalDistance = getHyperFocalDistance(characteristics),
hasFlash = hasFlashUnit(characteristics),
hasOpticalStabilization = hasOpticalStabilization(characteristics),
hasVideoStabilization = hasVideoStabilization(characteristics),
hasAutoFocus = hasAutoFocus(characteristics),
camera2ApiSupport = getCamera2ApiSupport(characteristics),
maxFaceCount = getMaxFaceCount(characteristics),
faceDetectionModes = getFaceDetectionModes(characteristics),
capabilities = getAvailableCapabilities(characteristics),
supportedFeatures = getSupportedFeatures(characteristics),
autoExposureModes = getAutoExposureModes(characteristics),
autoFocusModes = getAutoFocusModes(characteristics),
whiteBalanceModes = getWhiteBalanceModes(characteristics),
sceneModes = getSceneModes(characteristics),
colorEffects = getColorEffects(characteristics),
maxFrameRate = getMaxFrameRate(characteristics),
supportsHighSpeedVideo = supportsHighSpeedVideo(characteristics),
supports4K = supports4K(characteristics),
supports8K = supports8K(characteristics)
)
}
/**
* 获取所有摄像头的详细信息列表
*/
fun getAllCameraDetails(): List<CameraDetails> {
return cameraIds.mapNotNull { getCameraDetails(it) }
}
}

View File

@ -1,5 +1,6 @@
package com.xyzshell.andinfo.libs
import android.os.Build
import com.xyzshell.andinfo.libs.cpu.models.Cache
import com.xyzshell.andinfo.libs.cpu.models.Cluster
import com.xyzshell.andinfo.libs.cpu.models.Core
@ -7,60 +8,640 @@ import com.xyzshell.andinfo.libs.cpu.models.Package
import com.xyzshell.andinfo.libs.cpu.models.Processor
import com.xyzshell.andinfo.libs.cpu.models.UarchInfo
import com.xyzshell.andinfo.libs.cpu.utils.CpuInfoUtils
import java.io.File
/**
* CPU 信息类
* 用于获取设备处理器的详细信息包括核心数频率架构缓存等
*/
class CpuInfo {
// ==================== 基础处理器信息 ====================
/**
* 获取所有逻辑处理器列表
* @return 逻辑处理器列表
*/
val processors: List<Processor>
get() = CpuInfoUtils.getProcessors()
/**
* 获取所有物理核心列表
* @return 物理核心列表
*/
val cores: List<Core>
get() = CpuInfoUtils.getCores()
/**
* 获取所有核心集群列表大小核分组
* @return 集群列表
*/
val clusters: List<Cluster>
get() = CpuInfoUtils.getClusters()
/**
* 获取所有物理封装芯片列表
* @return 封装列表
*/
val packages: List<Package>
get() = CpuInfoUtils.getPackages()
/**
* 获取所有微架构信息列表
* @return 微架构信息列表
*/
val uarchs: List<UarchInfo>
get() = CpuInfoUtils.getUarchs()
// ==================== 缓存信息 ====================
/**
* L1 指令缓存列表
* @return L1i 缓存列表
*/
val l1iCaches: List<Cache>
get() = CpuInfoUtils.getL1iCaches()
/**
* L1 数据缓存列表
* @return L1d 缓存列表
*/
val l1dCaches: List<Cache>
get() = CpuInfoUtils.getL1dCaches()
/**
* L2 缓存列表
* @return L2 缓存列表
*/
val l2Caches: List<Cache>
get() = CpuInfoUtils.getL2Caches()
/**
* L3 缓存列表
* @return L3 缓存列表
*/
val l3Caches: List<Cache>
get() = CpuInfoUtils.getL3Caches()
/**
* L4 缓存列表
* @return L4 缓存列表
*/
val l4Caches: List<Cache>
get() = CpuInfoUtils.getL4Caches()
fun hardware(): String {
return if (processors.isNotEmpty()) {
processors[0].cpuPackage?.name ?: "未知"
// ==================== 处理器名称和供应商 ====================
/**
* 获取处理器名称SoC 型号
* @return 处理器名称例如 "Qualcomm Snapdragon 888"
*/
fun getProcessorName(): String {
return if (packages.isNotEmpty()) {
packages[0].name
} else {
"未知"
}
}
/**
* 获取处理器供应商
* @return 供应商名称例如 "Qualcomm", "ARM", "Intel"
*/
fun getVendor(): String {
return if (cores.isNotEmpty()) {
cores[0].vendor.name
} else {
"未知"
}
}
// ==================== 核心数量信息 ====================
/**
* 获取物理核心总数
* @return 核心数量
*/
fun getCoreCount(): Int {
return cores.size
}
/**
* 获取逻辑处理器总数
* @return 逻辑处理器数量
*/
fun getProcessorCount(): Int {
return processors.size
}
// ==================== 大小核信息 ====================
/**
* 大小核信息数据类
* @property bigCoreCount 大核数量
* @property midCoreCount 中核数量
* @property littleCoreCount 小核数量
* @property bigCoreFreq 大核最高频率Hz
* @property midCoreFreq 中核最高频率Hz
* @property littleCoreFreq 小核最高频率Hz
*/
data class ClusterInfo(
val bigCoreCount: Int,
val midCoreCount: Int,
val littleCoreCount: Int,
val bigCoreFreq: ULong,
val midCoreFreq: ULong,
val littleCoreFreq: ULong
)
/**
* 获取大小核信息
* @return 大小核信息对象
*/
fun getClusterInfo(): ClusterInfo {
if (clusters.isEmpty()) {
return ClusterInfo(0, 0, 0, 0UL, 0UL, 0UL)
}
// 按频率排序集群
val sortedClusters = clusters.sortedByDescending { cluster ->
cores.filter { it.cluster.clusterId == cluster.clusterId }
.maxOfOrNull { it.frequency } ?: 0UL
}
return when (sortedClusters.size) {
1 -> ClusterInfo(
0, 0, sortedClusters[0].coreCount.toInt(),
0UL, 0UL,
cores.filter { it.cluster.clusterId == sortedClusters[0].clusterId }
.maxOfOrNull { it.frequency } ?: 0UL
)
2 -> ClusterInfo(
sortedClusters[0].coreCount.toInt(),
0,
sortedClusters[1].coreCount.toInt(),
cores.filter { it.cluster.clusterId == sortedClusters[0].clusterId }
.maxOfOrNull { it.frequency } ?: 0UL,
0UL,
cores.filter { it.cluster.clusterId == sortedClusters[1].clusterId }
.maxOfOrNull { it.frequency } ?: 0UL
)
else -> ClusterInfo(
sortedClusters[0].coreCount.toInt(),
sortedClusters[1].coreCount.toInt(),
sortedClusters[2].coreCount.toInt(),
cores.filter { it.cluster.clusterId == sortedClusters[0].clusterId }
.maxOfOrNull { it.frequency } ?: 0UL,
cores.filter { it.cluster.clusterId == sortedClusters[1].clusterId }
.maxOfOrNull { it.frequency } ?: 0UL,
cores.filter { it.cluster.clusterId == sortedClusters[2].clusterId }
.maxOfOrNull { it.frequency } ?: 0UL
)
}
}
// ==================== 制程和架构 ====================
/**
* 制程信息数据类
* @property process 制程工艺例如 "4nm", "5nm", "7nm"
* @property foundry 代工厂例如 "台积电", "三星"
* @property node 详细制程节点例如 "TSMC N4P", "Samsung 4LPE"
*/
data class ProcessInfo(
val process: String,
val foundry: String,
val node: String = ""
)
/**
* 获取制程信息根据 SoC 型号查询
* @return 制程信息对象包含制程工艺和代工厂
*/
fun getProcessInfo(): ProcessInfo {
val processorName = getProcessorName().lowercase()
return when {
// 高通 Snapdragon 8 系列
"snapdragon 8 gen 3" in processorName || "sm8650" in processorName ->
ProcessInfo("4nm", "台积电", "TSMC N4P")
"snapdragon 8 gen 2" in processorName || "sm8550" in processorName ->
ProcessInfo("4nm", "台积电", "TSMC N4")
"snapdragon 8 gen 1" in processorName || "sm8450" in processorName ->
ProcessInfo("4nm", "三星", "Samsung 4LPE")
"snapdragon 888+" in processorName || "sm8350-ab" in processorName ->
ProcessInfo("5nm", "三星", "Samsung 5LPE")
"snapdragon 888" in processorName || "sm8350" in processorName ->
ProcessInfo("5nm", "三星", "Samsung 5LPE")
"snapdragon 870" in processorName || "sm8250-ac" in processorName ->
ProcessInfo("7nm", "台积电", "TSMC N7P")
"snapdragon 865+" in processorName || "sm8250-ab" in processorName ->
ProcessInfo("7nm", "台积电", "TSMC N7P")
"snapdragon 865" in processorName || "sm8250" in processorName ->
ProcessInfo("7nm", "台积电", "TSMC N7P")
// 高通 Snapdragon 7 系列
"snapdragon 7+ gen 2" in processorName || "sm7475" in processorName ->
ProcessInfo("4nm", "台积电", "TSMC N4")
"snapdragon 778g" in processorName || "sm7325" in processorName ->
ProcessInfo("6nm", "台积电", "TSMC N6")
// 高通 Snapdragon 6 系列
"snapdragon 695" in processorName || "sm6375" in processorName ->
ProcessInfo("6nm", "台积电", "TSMC N6")
// 联发科 Dimensity 9000 系列
"dimensity 9300" in processorName || "mt6989" in processorName ->
ProcessInfo("4nm", "台积电", "TSMC N4P")
"dimensity 9200+" in processorName || "mt6985" in processorName ->
ProcessInfo("4nm", "台积电", "TSMC N4")
"dimensity 9200" in processorName || "mt6983" in processorName ->
ProcessInfo("4nm", "台积电", "TSMC N4")
"dimensity 9000+" in processorName || "mt6985" in processorName ->
ProcessInfo("4nm", "台积电", "TSMC N4")
"dimensity 9000" in processorName || "mt6983" in processorName ->
ProcessInfo("4nm", "台积电", "TSMC N4")
// 联发科 Dimensity 8000 系列
"dimensity 8300" in processorName || "mt6897" in processorName ->
ProcessInfo("4nm", "台积电", "TSMC N4")
"dimensity 8200" in processorName || "mt6896" in processorName ->
ProcessInfo("4nm", "台积电", "TSMC N4")
"dimensity 8100" in processorName || "mt6895" in processorName ->
ProcessInfo("5nm", "台积电", "TSMC N5")
// 联发科 Dimensity 1000 系列
"dimensity 1200" in processorName || "mt6893" in processorName ->
ProcessInfo("6nm", "台积电", "TSMC N6")
"dimensity 1080" in processorName || "mt6877" in processorName ->
ProcessInfo("6nm", "台积电", "TSMC N6")
// 联发科 Helio 系列
"helio g99" in processorName || "mt6789" in processorName ->
ProcessInfo("6nm", "台积电", "TSMC N6")
// 三星 Exynos
"exynos 2400" in processorName || "s5e9945" in processorName ->
ProcessInfo("4nm", "三星", "Samsung 4LPP+")
"exynos 2200" in processorName || "s5e9925" in processorName ->
ProcessInfo("4nm", "三星", "Samsung 4LPE")
"exynos 2100" in processorName || "s5e9840" in processorName ->
ProcessInfo("5nm", "三星", "Samsung 5LPE")
"exynos 1080" in processorName || "s5e1080" in processorName ->
ProcessInfo("5nm", "三星", "Samsung 5LPE")
"exynos 1380" in processorName || "s5e8835" in processorName ->
ProcessInfo("5nm", "三星", "Samsung 5LPE")
"exynos 1280" in processorName || "s5e8825" in processorName ->
ProcessInfo("5nm", "三星", "Samsung 5LPE")
// 谷歌 Tensor
"tensor g3" in processorName || "gs301" in processorName ->
ProcessInfo("4nm", "三星", "Samsung 4LPP+")
"tensor g2" in processorName || "gs201" in processorName ->
ProcessInfo("5nm", "三星", "Samsung 5LPE")
"tensor g1" in processorName || "gs101" in processorName ->
ProcessInfo("5nm", "三星", "Samsung 5LPE")
"tensor" in processorName ->
ProcessInfo("5nm", "三星", "Samsung 5LPE")
// 华为麒麟
"kirin 9000" in processorName ->
ProcessInfo("5nm", "台积电", "TSMC N5")
"kirin 9000e" in processorName ->
ProcessInfo("5nm", "台积电", "TSMC N5")
"kirin 990" in processorName ->
ProcessInfo("7nm", "台积电", "TSMC N7+")
"kirin 980" in processorName ->
ProcessInfo("7nm", "台积电", "TSMC N7")
"kirin 810" in processorName ->
ProcessInfo("7nm", "台积电", "TSMC N7")
// 苹果 A 系列(参考)
"apple a17" in processorName ->
ProcessInfo("3nm", "台积电", "TSMC N3B")
"apple a16" in processorName ->
ProcessInfo("4nm", "台积电", "TSMC N4")
"apple a15" in processorName ->
ProcessInfo("5nm", "台积电", "TSMC N5P")
"apple a14" in processorName ->
ProcessInfo("5nm", "台积电", "TSMC N5")
// 默认情况
else -> ProcessInfo("未知", "未知", "")
}
}
/**
* 获取制程信息字符串格式兼容旧版本
* @return 制程信息例如 "5nm", "7nm"
*/
fun getProcess(): String {
return getProcessInfo().process
}
/**
* 获取代工厂信息
* @return 代工厂名称例如 "台积电", "三星"
*/
fun getFoundry(): String {
return getProcessInfo().foundry
}
/**
* 获取详细制程节点
* @return 制程节点例如 "TSMC N4P", "Samsung 5LPE"
*/
fun getProcessNode(): String {
return getProcessInfo().node.ifEmpty { getProcessInfo().process }
}
/**
* 获取 CPU 架构
* @return 架构名称例如 "Cortex-A78", "Cortex-X1"
*/
fun getArchitecture(): String {
return if (cores.isNotEmpty()) {
cores[0].uarch.name
} else {
"未知"
}
}
/**
* 获取所有核心的架构列表去重
* @return 架构列表
*/
fun getAllArchitectures(): List<String> {
return cores.map { it.uarch.name }.distinct()
}
// ==================== ABI 信息 ====================
/**
* 获取当前设备的主 ABI
* @return ABI 字符串例如 "arm64-v8a", "armeabi-v7a"
*/
fun getAbi(): String {
return Build.SUPPORTED_ABIS.firstOrNull() ?: "未知"
}
/**
* 获取所有支持的 ABI 列表
* @return ABI 列表
*/
fun getSupportedAbis(): List<String> {
return Build.SUPPORTED_ABIS.toList()
}
// ==================== 频率信息 ====================
/**
* 单个核心的频率信息
* @property coreId 核心 ID
* @property currentFreq 当前频率KHz
* @property minFreq 最小频率KHz
* @property maxFreq 最大频率KHz
* @property availableFreqs 可用频率列表KHz
*/
data class CoreFrequencyInfo(
val coreId: Int,
val currentFreq: Long,
val minFreq: Long,
val maxFreq: Long,
val availableFreqs: List<Long>
)
/**
* 获取所有核心的详细频率信息
* @return 核心频率信息列表
*/
fun getCoreFrequencies(): List<CoreFrequencyInfo> {
val result = mutableListOf<CoreFrequencyInfo>()
for (i in 0 until getCoreCount()) {
val currentFreq = readFrequency("/sys/devices/system/cpu/cpu$i/cpufreq/scaling_cur_freq")
val minFreq = readFrequency("/sys/devices/system/cpu/cpu$i/cpufreq/cpuinfo_min_freq")
val maxFreq = readFrequency("/sys/devices/system/cpu/cpu$i/cpufreq/cpuinfo_max_freq")
val availableFreqs = readAvailableFrequencies("/sys/devices/system/cpu/cpu$i/cpufreq/scaling_available_frequencies")
result.add(CoreFrequencyInfo(i, currentFreq, minFreq, maxFreq, availableFreqs))
}
return result
}
/**
* 读取频率值
* @param path 频率文件路径
* @return 频率值KHz失败返回 0
*/
private fun readFrequency(path: String): Long {
return try {
File(path).readText().trim().toLongOrNull() ?: 0L
} catch (e: Exception) {
0L
}
}
/**
* 读取可用频率列表
* @param path 可用频率文件路径
* @return 频率列表KHz
*/
private fun readAvailableFrequencies(path: String): List<Long> {
return try {
File(path).readText().trim()
.split("\\s+".toRegex())
.mapNotNull { it.toLongOrNull() }
} catch (e: Exception) {
emptyList()
}
}
// ==================== 调频器信息 ====================
/**
* 获取指定核心的调频器
* @param coreId 核心 ID
* @return 调频器名称例如 "schedutil", "interactive"
*/
fun getGovernor(coreId: Int): String {
return try {
File("/sys/devices/system/cpu/cpu$coreId/cpufreq/scaling_governor")
.readText().trim()
} catch (e: Exception) {
"未知"
}
}
/**
* 获取所有核心的调频器
* @return 调频器列表
*/
fun getAllGovernors(): List<String> {
return (0 until getCoreCount()).map { getGovernor(it) }
}
/**
* 获取可用的调频器列表
* @param coreId 核心 ID
* @return 可用调频器列表
*/
fun getAvailableGovernors(coreId: Int = 0): List<String> {
return try {
File("/sys/devices/system/cpu/cpu$coreId/cpufreq/scaling_available_governors")
.readText().trim()
.split("\\s+".toRegex())
} catch (e: Exception) {
emptyList()
}
}
// ==================== CPU 特性 ====================
/**
* 获取 CPU 特性列表 /proc/cpuinfo 读取 Features
* @return CPU 特性列表
*/
fun getCpuFeatures(): List<String> {
return try {
File("/proc/cpuinfo").readLines()
.find { it.startsWith("Features") }
?.substringAfter(":")
?.trim()
?.split("\\s+".toRegex()) ?: emptyList()
} catch (e: Exception) {
emptyList()
}
}
// ==================== /proc/cpuinfo 原始信息 ====================
/**
* 获取 /proc/cpuinfo 的完整内容
* @return /proc/cpuinfo 文件内容
*/
fun getProcCpuInfo(): String {
return try {
File("/proc/cpuinfo").readText()
} catch (e: Exception) {
"无法读取 /proc/cpuinfo"
}
}
/**
* 解析 /proc/cpuinfo 为键值对映射
* @return 解析后的映射表
*/
fun parseProcCpuInfo(): Map<String, String> {
val result = mutableMapOf<String, String>()
try {
File("/proc/cpuinfo").readLines().forEach { line ->
if (line.contains(":")) {
val parts = line.split(":", limit = 2)
if (parts.size == 2) {
result[parts[0].trim()] = parts[1].trim()
}
}
}
} catch (e: Exception) {
// 忽略错误
}
return result
}
// ==================== 兼容性方法 ====================
/**
* 获取硬件名称兼容旧版本
* @return 处理器名称
*/
@Deprecated("使用 getProcessorName() 替代", ReplaceWith("getProcessorName()"))
fun hardware(): String {
return getProcessorName()
}
/**
* 获取文本格式的 CPU 信息摘要
* @return 格式化的文本信息
*/
fun text(): String {
val stringBuilder = StringBuilder()
stringBuilder.append("处理器数量: ${processors.size}\n")
processors.forEachIndexed { index, processor ->
stringBuilder.append(" 处理器 ${index}: ${processor.cpuPackage?.name ?: "未知"}\n")
stringBuilder.append(" 核心: ${processor.core?.coreId ?: "未知"}\n")
stringBuilder.append(" 集群: ${processor.cluster?.clusterId ?: "未知"}\n")
val sb = StringBuilder()
// 基本信息
sb.append("=== CPU 基本信息 ===\n")
sb.append("处理器名称: ${getProcessorName()}\n")
sb.append("供应商: ${getVendor()}\n")
val processInfo = getProcessInfo()
sb.append("制程工艺: ${processInfo.process}\n")
sb.append("代工厂: ${processInfo.foundry}\n")
if (processInfo.node.isNotEmpty()) {
sb.append("制程节点: ${processInfo.node}\n")
}
stringBuilder.append("核心数量: ${cores.size}\n")
cores.forEachIndexed { index, core ->
stringBuilder.append(" 核心 ${index}: ID=${core.coreId}, 频率=${core.frequency}Hz\n")
sb.append("物理核心数: ${getCoreCount()}\n")
sb.append("逻辑处理器数: ${getProcessorCount()}\n")
// 大小核信息
val clusterInfo = getClusterInfo()
sb.append("\n=== 大小核配置 ===\n")
if (clusterInfo.bigCoreCount > 0) {
sb.append("大核: ${clusterInfo.bigCoreCount} 个, 最高频率: ${formatFrequency(clusterInfo.bigCoreFreq)}\n")
}
if (clusterInfo.midCoreCount > 0) {
sb.append("中核: ${clusterInfo.midCoreCount} 个, 最高频率: ${formatFrequency(clusterInfo.midCoreFreq)}\n")
}
if (clusterInfo.littleCoreCount > 0) {
sb.append("小核: ${clusterInfo.littleCoreCount} 个, 最高频率: ${formatFrequency(clusterInfo.littleCoreFreq)}\n")
}
// 架构信息
sb.append("\n=== 架构信息 ===\n")
sb.append("架构: ${getAllArchitectures().joinToString(", ")}\n")
sb.append("ABI: ${getAbi()}\n")
sb.append("支持的 ABI: ${getSupportedAbis().joinToString(", ")}\n")
// 调频器
sb.append("\n=== 调频器 ===\n")
sb.append("当前调频器: ${getGovernor(0)}\n")
sb.append("可用调频器: ${getAvailableGovernors().joinToString(", ")}\n")
// 核心详细频率
sb.append("\n=== 核心频率信息 ===\n")
getCoreFrequencies().forEach { freq ->
sb.append("CPU${freq.coreId}: 当前=${formatFrequency(freq.currentFreq.toULong() * 1000UL)}, ")
sb.append("范围=${formatFrequency(freq.minFreq.toULong() * 1000UL)}-${formatFrequency(freq.maxFreq.toULong() * 1000UL)}\n")
}
// 缓存信息
sb.append("\n=== 缓存信息 ===\n")
sb.append("L1i 缓存: ${l1iCaches.size}\n")
sb.append("L1d 缓存: ${l1dCaches.size}\n")
sb.append("L2 缓存: ${l2Caches.size}\n")
sb.append("L3 缓存: ${l3Caches.size}\n")
// CPU 特性
val features = getCpuFeatures()
if (features.isNotEmpty()) {
sb.append("\n=== CPU 特性 ===\n")
sb.append(features.joinToString(" "))
sb.append("\n")
}
return sb.toString()
}
/**
* 格式化频率显示
* @param freqHz 频率Hz
* @return 格式化后的频率字符串
*/
private fun formatFrequency(freqHz: ULong): String {
return when {
freqHz >= 1_000_000_000UL -> String.format("%.2f GHz", freqHz.toDouble() / 1_000_000_000)
freqHz >= 1_000_000UL -> String.format("%.0f MHz", freqHz.toDouble() / 1_000_000)
freqHz >= 1_000UL -> String.format("%.0f KHz", freqHz.toDouble() / 1_000)
else -> "$freqHz Hz"
}
// 可以根据需要添加更多详细信息
return stringBuilder.toString()
}
companion object {

View File

@ -6,11 +6,20 @@ import android.os.Build
import android.view.Display
import android.graphics.Point
import android.util.DisplayMetrics
import kotlin.math.sqrt
/**
* 显示信息类用于获取设备屏幕相关信息
*/
class DisplayInfo(private val context: Context) {
private val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
/**
* 获取默认显示器信息
* @return 默认显示器信息对象如果获取失败返回 null
*/
fun getDefaultDisplayInfo(): DefaultDisplayInfo? {
val defaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY) ?: return null
@ -22,13 +31,24 @@ class DisplayInfo(private val context: Context) {
val mode = defaultDisplay.mode
val refreshRate = mode.refreshRate
val isHdr = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
defaultDisplay.hdrCapabilities?.supportedHdrTypes?.isNotEmpty() ?: false
// 计算 PPI (每英寸像素数)
val ppi = calculatePPI(displayMetrics.widthPixels, displayMetrics.heightPixels, displayMetrics.densityDpi)
// 获取支持的刷新率列表
val supportedRefreshRates = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
defaultDisplay.supportedModes.map { it.refreshRate }.distinct().sorted()
} else {
false
listOf(refreshRate)
}
// 检查是否支持 HDR 及 HDR 类型
val hdrTypes = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
defaultDisplay.mode?.supportedHdrTypes?.map { getHdrTypeName(it) } ?: emptyList()
} else {
emptyList()
}
val isHdr = hdrTypes.isNotEmpty()
// 检查是否支持广色域
val isWideColorGamut = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
defaultDisplay.isWideColorGamut
} else {
@ -43,22 +63,81 @@ class DisplayInfo(private val context: Context) {
densityDpi = displayMetrics.densityDpi,
xdpi = displayMetrics.xdpi,
ydpi = displayMetrics.ydpi,
ppi = ppi,
refreshRate = refreshRate,
supportedRefreshRates = supportedRefreshRates,
isHdr = isHdr,
hdrTypes,
isWideColorGamut = isWideColorGamut
)
}
/**
* 获取 HDR 类型名称
* @param hdrType HDR 类型常量
* @return HDR 类型名称
*/
private fun getHdrTypeName(hdrType: Int): String {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
when (hdrType) {
Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION -> "Dolby Vision"
Display.HdrCapabilities.HDR_TYPE_HDR10 -> "HDR10"
Display.HdrCapabilities.HDR_TYPE_HLG -> "HLG"
Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS -> "HDR10+"
else -> "Unknown HDR Type ($hdrType)"
}
} else {
"Unknown"
}
}
/**
* 计算屏幕 PPI (每英寸像素数)
* @param widthPixels 屏幕宽度像素
* @param heightPixels 屏幕高度像素
* @param densityDpi 屏幕密度 DPI
* @return PPI
*/
private fun calculatePPI(widthPixels: Int, heightPixels: Int, densityDpi: Int): Double {
// 使用勾股定理计算对角线像素数
val diagonalPixels = sqrt((widthPixels * widthPixels + heightPixels * heightPixels).toDouble())
// 通过 densityDpi 计算屏幕对角线英寸数
// 160 DPI 是 Android 的基准密度 (mdpi)
val diagonalInches = diagonalPixels / densityDpi
// PPI = 对角线像素数 / 对角线英寸数
return diagonalPixels / diagonalInches
}
/**
* 默认显示器信息数据类
* @property id 显示器 ID
* @property name 显示器名称
* @property widthPixels 屏幕宽度像素
* @property heightPixels 屏幕高度像素
* @property densityDpi 屏幕密度 DPI
* @property xdpi X 轴每英寸像素数
* @property ydpi Y 轴每英寸像素数
* @property ppi 屏幕 PPI每英寸像素数
* @property refreshRate 当前刷新率Hz
* @property supportedRefreshRates 支持的刷新率列表Hz
* @property isHdr 是否支持 HDR
* @property hdrTypes 支持的 HDR 类型列表
* @property isWideColorGamut 是否支持广色域
*/
data class DefaultDisplayInfo(
val id: Int,
val name: String,
val widthPixels: Int,
val heightPixels: Int,
val densityDpi: Int,
val xdpi: Float,
val ydpi: Float,
val refreshRate: Float,
val isHdr: Boolean,
val isWideColorGamut: Boolean
val id: Int, // 显示器 ID
val name: String, // 显示器名称
val widthPixels: Int, // 屏幕宽度(像素)
val heightPixels: Int, // 屏幕高度(像素)
val densityDpi: Int, // 屏幕密度 DPI
val xdpi: Float, // X 轴 DPI
val ydpi: Float, // Y 轴 DPI
val ppi: Double, // 屏幕 PPI
val refreshRate: Float, // 当前刷新率Hz
val supportedRefreshRates: List<Float>, // 支持的刷新率列表Hz
val isHdr: Boolean, // 是否支持 HDR
val hdrTypes: List<String>, // 支持的 HDR 类型列表
val isWideColorGamut: Boolean // 是否支持广色域
)
}

View File

@ -1,4 +1,142 @@
package com.xyzshell.andinfo.libs
class MemInfo {
import android.content.Context
import java.io.File
import java.io.RandomAccessFile
data class MemoryInfo(
val totalRam: Long, // 总内存 (字节)
val availableRam: Long, // 可用内存 (字节)
val usedRam: Long, // 已使用内存 (字节)
val memType: String, // 内存类型 (LPDDR4/LPDDR5等)
val channels: Int, // 内存通道数
val zramTotal: Long, // zram 总大小 (字节)
val zramUsed: Long // zram 已使用 (字节)
)
class MemInfo(private val context: Context){
fun getMemoryInfo(): MemoryInfo {
val totalRam = getTotalMemory()
val availableRam = getAvailableMemory()
val usedRam = totalRam - availableRam
val memType = getMemoryType()
val channels = getMemoryChannels()
val (zramTotal, zramUsed) = getZramInfo()
return MemoryInfo(
totalRam = totalRam,
availableRam = availableRam,
usedRam = usedRam,
memType = memType,
channels = channels,
zramTotal = zramTotal,
zramUsed = zramUsed
)
}
private fun getTotalMemory(): Long {
return try {
val memInfo = File("/proc/meminfo")
memInfo.readLines().find { it.startsWith("MemTotal:") }?.let {
it.split("\\s+".toRegex())[1].toLong() * 1024
} ?: 0L
} catch (e: Exception) {
0L
}
}
private fun getAvailableMemory(): Long {
return try {
val memInfo = File("/proc/meminfo")
memInfo.readLines().find { it.startsWith("MemAvailable:") }?.let {
it.split("\\s+".toRegex())[1].toLong() * 1024
} ?: 0L
} catch (e: Exception) {
0L
}
}
private fun getMemoryType(): String {
return try {
// 尝试从多个可能的位置读取
val paths = listOf(
"/sys/class/devfreq/ddrfreq/device/type",
"/sys/class/devfreq/soc:qcom,cpu-cpu-llcc-bw/device/type",
"/proc/device-tree/memory/device_type"
)
for (path in paths) {
val file = File(path)
if (file.exists()) {
val content = file.readText().trim()
if (content.isNotEmpty()) {
return content
}
}
}
// 尝试从 getprop 获取
Runtime.getRuntime().exec("getprop ro.boot.ddr_type").inputStream.bufferedReader().readText().trim()
.ifEmpty { "Unknown" }
} catch (e: Exception) {
"Unknown"
}
}
private fun getMemoryChannels(): Int {
return try {
// 尝试从设备树或内核日志获取
val dmesgChannels = Runtime.getRuntime()
.exec("dmesg | grep -i 'memory.*channel'")
.inputStream.bufferedReader().readText()
// 解析通道数,默认返回 2
when {
dmesgChannels.contains("dual", ignoreCase = true) -> 2
dmesgChannels.contains("quad", ignoreCase = true) -> 4
dmesgChannels.contains("single", ignoreCase = true) -> 1
else -> 2 // 默认双通道
}
} catch (e: Exception) {
2
}
}
private fun getZramInfo(): Pair<Long, Long> {
return try {
var total = 0L
var used = 0L
// 检查所有 zram 设备
for (i in 0..7) {
val devicePath = "/sys/block/zram$i"
if (!File(devicePath).exists()) continue
// 读取总大小
File("$devicePath/disksize").readText().trim().toLongOrNull()?.let {
total += it
}
// 读取已使用大小
File("$devicePath/mem_used_total").readText().trim().toLongOrNull()?.let {
used += it
}
}
Pair(total, used)
} catch (e: Exception) {
Pair(0L, 0L)
}
}
// 格式化内存大小
fun formatBytes(bytes: Long): String {
return when {
bytes < 1024 -> "$bytes B"
bytes < 1024 * 1024 -> String.format("%.2f KB", bytes / 1024.0)
bytes < 1024 * 1024 * 1024 -> String.format("%.2f MB", bytes / (1024.0 * 1024))
else -> String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024))
}
}
}

View File

@ -0,0 +1,511 @@
package com.xyzshell.andinfo.libs
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.net.*
import android.net.wifi.ScanResult
import android.net.wifi.WifiManager
import android.net.wifi.WifiInfo
import android.os.Build
import android.telephony.*
import androidx.annotation.RequiresPermission
import androidx.core.app.ActivityCompat
import kotlin.text.compareTo
/**
* 网络信息工具类提供当前网络类型Wi-Fi详情移动数据详情等获取方法
*/
class NetworkInfo(private val context: Context) {
/**
* 当前网络类型枚举
*/
enum class NetworkType {
WIFI, MOBILE, NONE
}
/**
* Wi-Fi详细信息数据结构
*/
data class WifiDetails(
val connected: Boolean, // 是否已连接
val ssid: String?, // SSID名称
val bssid: String?, // BSSID
val capabilities: String?, // 加密/功能信息
val linkSpeedMbps: Int?, // 连接速率
val rssi: Int?, // dBm信号强度
val signalLevelPercent: Int?, // 信号强度百分比
val frequency: Int?, // 频率(MHz)
val channel: Int?, // 信道
val standard: String?, // 当前连接的标准
val dhcpServer: String?, // DHCP服务器
val leaseDuration: Int?, // DHCP租约期限(秒)
val gateway: String?, // 网关
val subnetMask: String?, // 子网掩码
val dns1: String?, // DNS1
val dns2: String?, // DNS2
val ip: String?, // IPv4地址
val ipv6: List<String>?, // IPv6地址列表
val supportedStandards: List<String>?, // 硬件支持的Wi-Fi标准
val wifiDirect: Boolean?, // 是否支持Wi-Fi Direct
val wifiAware: Boolean?, // 是否支持Wi-Fi Aware
val wifiPasspoint: Boolean?, // 是否支持Passpoint
val support5G: Boolean?, // 是否支持5GHz Wi-Fi
val support6G: Boolean?, // 是否支持6GHz Wi-Fi
val support60G: Boolean? // 是否支持60GHz Wi-Fi
)
/**
* SIM卡信息数据结构
*/
data class SimInfo(
val slotIndex: Int, // 卡槽索引
val carrierName: String?, // 运营商名称
val operator: String?, // 运营商代码(MCC+MNC)
val countryIso: String?, // 国家代码
val roaming: Boolean, // 是否漫游
val simState: Int, // SIM卡状态
val simStateText: String, // SIM卡状态文本
val phoneType: Int, // 手机类型
val phoneTypeText: String, // 手机类型文本
val networkType: Int, // 网络类型
val networkTypeText: String, // 网络类型文本
val isEsim: Boolean // 是否eSIM
)
/**
* 移动数据详细信息数据结构
*/
data class MobileDetails(
val dualSim: Boolean, // 是否双卡
val phoneCount: Int, // 卡槽数量
val phoneType: Int, // 手机类型
val esimSupported: Boolean, // 是否支持eSIM
val dataEnabled: Boolean, // 移动数据是否开启
val dataSim: Int?, // 默认数据网络类型
val voiceSim: Int?, // 默认语音网络类型
val smsSim: Int?, // 默认短信卡(Android无直接API)
val simInfos: List<SimInfo> // SIM卡信息列表
)
/**
* 获取当前网络类型Wi-Fi/移动数据/无网络
*/
fun getCurrentNetworkType(): NetworkType {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
?: return NetworkType.NONE
return try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val active = cm.activeNetwork ?: return NetworkType.NONE
val caps = cm.getNetworkCapabilities(active) ?: return NetworkType.NONE
when {
caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> NetworkType.WIFI
caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> NetworkType.MOBILE
else -> NetworkType.NONE
}
} else {
@Suppress("DEPRECATION")
when (cm.activeNetworkInfo?.type) {
ConnectivityManager.TYPE_WIFI -> NetworkType.WIFI
ConnectivityManager.TYPE_MOBILE -> NetworkType.MOBILE
else -> NetworkType.NONE
}
}
} catch (e: Exception) {
NetworkType.NONE
}
}
/**
* 获取当前Wi-Fi详细信息
* 需要权限: ACCESS_WIFI_STATE, (Android 10+)
*/
fun getWifiDetails(): WifiDetails? {
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as? WifiManager
?: return null
// 检查权限
if (!checkWifiPermissions()) {
return null
}
return try {
val wifiInfo = wifiManager.connectionInfo ?: return createEmptyWifiDetails()
val connected = wifiInfo.networkId != -1
if (!connected) return createEmptyWifiDetails()
val dhcp = wifiManager.dhcpInfo
val ssid = wifiInfo.ssid?.removePrefix("\"")?.removeSuffix("\"")
val bssid = wifiInfo.bssid
val linkSpeed = wifiInfo.linkSpeed
val rssi = wifiInfo.rssi
val freq = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
wifiInfo.frequency
} else null
val channel = freq?.let { calculateChannel(it) }
val level = if (rssi != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
WifiManager.calculateSignalLevel(rssi, 100)
} else {
@Suppress("DEPRECATION")
WifiManager.calculateSignalLevel(rssi, 100)
}
} else null
// 获取加密能力信息
val capabilities = try {
wifiManager.scanResults.find { it.BSSID == bssid }?.capabilities
} catch (e: SecurityException) {
null
}
// 获取当前Wi-Fi标准
val standard = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
when (wifiInfo.wifiStandard) {
ScanResult.WIFI_STANDARD_LEGACY -> "802.11a/b/g"
ScanResult.WIFI_STANDARD_11N -> "802.11n (Wi-Fi 4)"
ScanResult.WIFI_STANDARD_11AC -> "802.11ac (Wi-Fi 5)"
ScanResult.WIFI_STANDARD_11AX -> "802.11ax (Wi-Fi 6/6E)"
else -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
when (wifiInfo.wifiStandard) {
ScanResult.WIFI_STANDARD_11AD -> "802.11ad (WiGig)"
else -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
when (wifiInfo.wifiStandard) {
ScanResult.WIFI_STANDARD_11BE -> "802.11be (Wi-Fi 7)"
else -> "Unknown"
}
} else "Unknown"
}
} else "Unknown"
}
} else null
val dhcpServer = dhcp?.serverAddress?.let { intToIp(it) }
val leaseDuration = dhcp?.leaseDuration
val gateway = dhcp?.gateway?.let { intToIp(it) }
val subnetMask = dhcp?.netmask?.let { intToIp(it) }
val dns1 = dhcp?.dns1?.let { intToIp(it) }
val dns2 = dhcp?.dns2?.let { intToIp(it) }
val ip = dhcp?.ipAddress?.let { intToIp(it) }
val ipv6 = getIpv6Addresses()
val supportedStandards = getSupportedWifiStandards(wifiManager)
// Wi-Fi Direct (P2P) 支持
val wifiDirect = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)
} else null
// Wi-Fi Aware (NAN) 支持
val wifiAware = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)
} else null
// Passpoint (Hotspot 2.0) 支持
val wifiPasspoint = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
try {
wifiManager.isWifiPasspointEnabled
} catch (e: NoSuchMethodError) {
// 某些设备可能不支持此方法
null
} catch (e: Exception) {
null
}
} else null
val support5G = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
wifiManager.is5GHzBandSupported
} else null
val support6G = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
wifiManager.is6GHzBandSupported
} else null
val support60G = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
wifiManager.is60GHzBandSupported
} else null
WifiDetails(
connected, ssid, bssid, capabilities, linkSpeed, rssi, level, freq, channel, standard,
dhcpServer, leaseDuration, gateway, subnetMask, dns1, dns2, ip, ipv6,
supportedStandards, wifiDirect, wifiAware, wifiPasspoint, support5G, support6G, support60G
)
} catch (e: Exception) {
null
}
}
/**
* 获取移动数据详细信息包括双卡eSIMSIM卡状态等
* 需要权限: READ_PHONE_STATE
*/
@RequiresPermission(Manifest.permission.READ_PHONE_STATE)
fun getMobileDetails(): MobileDetails? {
if (!checkPhonePermissions()) {
return null
}
val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager
?: return null
return try {
val subscriptionManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as? SubscriptionManager
} else null
val sims = mutableListOf<SimInfo>()
val esimSupported = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
tm.isMultiSimSupported == TelephonyManager.MULTISIM_ALLOWED &&
context.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
} else false
val phoneType = tm.phoneType
val phoneCount = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
tm.phoneCount
} else 1
val dualSim = phoneCount > 1
val dataEnabled = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
tm.isDataEnabled
} else {
@Suppress("DEPRECATION")
tm.dataState == TelephonyManager.DATA_CONNECTED
}
val dataSim = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
tm.dataNetworkType
} else null
val voiceSim = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
tm.voiceNetworkType
} else null
val smsSim = null // Android没有直接获取默认短信卡的API
// 获取每个SIM卡的详细信息
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1 && subscriptionManager != null) {
val subInfoList = subscriptionManager.activeSubscriptionInfoList ?: emptyList()
for (info in subInfoList) {
val subTm = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
tm.createForSubscriptionId(info.subscriptionId)
} else tm
sims.add(
SimInfo(
slotIndex = info.simSlotIndex,
carrierName = info.carrierName?.toString(),
operator = subTm.simOperator,
countryIso = subTm.simCountryIso,
roaming = subTm.isNetworkRoaming,
simState = subTm.simState,
simStateText = getSimStateText(subTm.simState),
phoneType = subTm.phoneType,
phoneTypeText = getPhoneTypeText(subTm.phoneType),
networkType = subTm.networkType,
networkTypeText = getNetworkTypeText(subTm.networkType),
isEsim = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
info.isEmbedded
} else false
)
)
}
} else {
// 旧版本系统,只能获取单卡信息
sims.add(
SimInfo(
slotIndex = 0,
carrierName = tm.simOperatorName,
operator = tm.simOperator,
countryIso = tm.simCountryIso,
roaming = tm.isNetworkRoaming,
simState = tm.simState,
simStateText = getSimStateText(tm.simState),
phoneType = tm.phoneType,
phoneTypeText = getPhoneTypeText(tm.phoneType),
networkType = tm.networkType,
networkTypeText = getNetworkTypeText(tm.networkType),
isEsim = esimSupported
)
)
}
MobileDetails(
dualSim = dualSim,
phoneCount = phoneCount,
phoneType = phoneType,
esimSupported = esimSupported,
dataEnabled = dataEnabled,
dataSim = dataSim,
voiceSim = voiceSim,
smsSim = smsSim,
simInfos = sims
)
} catch (e: Exception) {
null
}
}
/**
* int型IP转为字符串
*/
private fun intToIp(ip: Int): String =
"${ip and 0xFF}.${ip shr 8 and 0xFF}.${ip shr 16 and 0xFF}.${ip shr 24 and 0xFF}"
/**
* 获取所有IPv6地址
*/
private fun getIpv6Addresses(): List<String> {
val result = mutableListOf<String>()
try {
val interfaces = java.net.NetworkInterface.getNetworkInterfaces()
for (iface in interfaces) {
if (!iface.isUp || iface.isLoopback) continue
for (addr in iface.inetAddresses) {
if (addr is java.net.Inet6Address && !addr.isLoopbackAddress && !addr.isLinkLocalAddress) {
result.add(addr.hostAddress ?: continue)
}
}
}
} catch (_: Exception) {}
return result
}
/**
* 获取设备支持的Wi-Fi标准
*/
private fun getSupportedWifiStandards(wifiManager: WifiManager): List<String> {
val list = mutableListOf<String>()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (wifiManager.isWifiStandardSupported(android.net.wifi.ScanResult.WIFI_STANDARD_LEGACY)) {
list.add("802.11a/b/g")
}
if (wifiManager.isWifiStandardSupported(android.net.wifi.ScanResult.WIFI_STANDARD_11N)) {
list.add("802.11n (Wi-Fi 4)")
}
if (wifiManager.isWifiStandardSupported(android.net.wifi.ScanResult.WIFI_STANDARD_11AC)) {
list.add("802.11ac (Wi-Fi 5)")
}
if (wifiManager.isWifiStandardSupported(android.net.wifi.ScanResult.WIFI_STANDARD_11AX)) {
list.add("802.11ax (Wi-Fi 6/6E)")
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (wifiManager.isWifiStandardSupported(android.net.wifi.ScanResult.WIFI_STANDARD_11AD)) {
list.add("802.11ad (WiGig)")
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (wifiManager.isWifiStandardSupported(android.net.wifi.ScanResult.WIFI_STANDARD_11BE)) {
list.add("802.11be (Wi-Fi 7)")
}
}
}
return list
}
/**
* 根据频率计算信道
*/
private fun calculateChannel(frequency: Int): Int {
return when {
frequency in 2412..2484 -> (frequency - 2407) / 5 // 2.4GHz
frequency in 5170..5825 -> (frequency - 5000) / 5 // 5GHz
frequency in 5955..7115 -> (frequency - 5950) / 5 // 6GHz
else -> -1
}
}
/**
* 创建空的Wi-Fi详情对象
*/
private fun createEmptyWifiDetails() = WifiDetails(
false, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null,
null, support60G = null
)
/**
* 检查Wi-Fi相关权限
*/
private fun checkWifiPermissions(): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
return ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_WIFI_STATE
) == PackageManager.PERMISSION_GRANTED
}
/**
* 检查电话相关权限
*/
private fun checkPhonePermissions(): Boolean {
return ActivityCompat.checkSelfPermission(
context,
Manifest.permission.READ_PHONE_STATE
) == PackageManager.PERMISSION_GRANTED
}
/**
* 获取SIM卡状态文本
*/
private fun getSimStateText(state: Int): String = when (state) {
TelephonyManager.SIM_STATE_ABSENT -> "无卡"
TelephonyManager.SIM_STATE_PIN_REQUIRED -> "需要PIN"
TelephonyManager.SIM_STATE_PUK_REQUIRED -> "需要PUK"
TelephonyManager.SIM_STATE_NETWORK_LOCKED -> "网络锁定"
TelephonyManager.SIM_STATE_READY -> "就绪"
TelephonyManager.SIM_STATE_NOT_READY -> "未就绪"
TelephonyManager.SIM_STATE_PERM_DISABLED -> "永久禁用"
TelephonyManager.SIM_STATE_CARD_IO_ERROR -> "卡IO错误"
TelephonyManager.SIM_STATE_CARD_RESTRICTED -> "卡受限"
else -> "未知"
}
/**
* 获取手机类型文本
*/
private fun getPhoneTypeText(type: Int): String = when (type) {
TelephonyManager.PHONE_TYPE_NONE -> ""
TelephonyManager.PHONE_TYPE_GSM -> "GSM"
TelephonyManager.PHONE_TYPE_CDMA -> "CDMA"
TelephonyManager.PHONE_TYPE_SIP -> "SIP"
else -> "未知"
}
/**
* 获取网络类型文本
*/
private fun getNetworkTypeText(type: Int): String = when (type) {
TelephonyManager.NETWORK_TYPE_GPRS -> "GPRS"
TelephonyManager.NETWORK_TYPE_EDGE -> "EDGE"
TelephonyManager.NETWORK_TYPE_UMTS -> "UMTS (3G)"
TelephonyManager.NETWORK_TYPE_CDMA -> "CDMA"
TelephonyManager.NETWORK_TYPE_EVDO_0 -> "EVDO 0"
TelephonyManager.NETWORK_TYPE_EVDO_A -> "EVDO A"
TelephonyManager.NETWORK_TYPE_1xRTT -> "1xRTT"
TelephonyManager.NETWORK_TYPE_HSDPA -> "HSDPA"
TelephonyManager.NETWORK_TYPE_HSUPA -> "HSUPA"
TelephonyManager.NETWORK_TYPE_HSPA -> "HSPA"
TelephonyManager.NETWORK_TYPE_IDEN -> "iDEN"
TelephonyManager.NETWORK_TYPE_EVDO_B -> "EVDO B"
TelephonyManager.NETWORK_TYPE_LTE -> "LTE (4G)"
TelephonyManager.NETWORK_TYPE_EHRPD -> "eHRPD"
TelephonyManager.NETWORK_TYPE_HSPAP -> "HSPA+"
TelephonyManager.NETWORK_TYPE_GSM -> "GSM"
TelephonyManager.NETWORK_TYPE_TD_SCDMA -> "TD-SCDMA"
TelephonyManager.NETWORK_TYPE_IWLAN -> "IWLAN"
else -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
when (type) {
TelephonyManager.NETWORK_TYPE_NR -> "5G NR"
else -> "未知"
}
} else "未知"
}
}

View File

@ -5,22 +5,27 @@ import android.os.Environment
import android.os.StatFs
import com.xyzshell.andinfo.libs.storage.models.EncryptionType
import com.xyzshell.andinfo.utils.SystemProperties
import java.io.File
class StorageInfo(private val context: Context) {
// 内部存储总空间
val internalStorageTotalSpace: Long
get() = StatFs(Environment.getDataDirectory().absolutePath).let { statFs ->
statFs.blockCountLong * statFs.blockSizeLong
}
// 内部存储可用空间
val internalStorageAvailableSpace: Long
get() = StatFs(Environment.getDataDirectory().absolutePath).let { statFs ->
statFs.availableBlocksLong * statFs.blockSizeLong
}
// 内部存储已使用空间
val internalStorageUsedSpace: Long
get() = internalStorageTotalSpace - internalStorageAvailableSpace
// 内部存储是否加密
val isInternalStorageEncrypted: Boolean?
get() = when (SystemProperties.getString("ro.crypto.state", "unknown")) {
"encrypted" -> true
@ -28,6 +33,7 @@ class StorageInfo(private val context: Context) {
else -> null
}
// 内部存储加密类型
val internalStorageEncryptionType: EncryptionType?
get() = when (SystemProperties.getString("ro.crypto.type", "unknown")) {
"none" -> EncryptionType.NONE
@ -36,6 +42,58 @@ class StorageInfo(private val context: Context) {
else -> null
}
// 内部存储文件系统类型
val internalStorageFileSystemType: String?
get() = try {
// 方法1: 读取 /proc/mounts 文件
File("/proc/mounts").readLines()
.find { it.contains(" /data ") }
?.split("\\s+".toRegex())
?.getOrNull(2)
?: run {
// 方法2: 使用 StatFs (Android 8.0+)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val statFs = StatFs("/data")
statFs.javaClass.getMethod("getFileSystemName").invoke(statFs) as? String
} else {
null
}
}
} catch (e: Exception) {
null
}
// /data 目录总大小
val dataDirectoryTotalSpace: Long
get() = StatFs("/data").let { statFs ->
statFs.blockCountLong * statFs.blockSizeLong
}
// /data 目录已使用空间
val dataDirectoryUsedSpace: Long
get() = StatFs("/data").let { statFs ->
val total = statFs.blockCountLong * statFs.blockSizeLong
val available = statFs.availableBlocksLong * statFs.blockSizeLong
total - available
}
// 应用程序和数据占用大小
val applicationsAndDataSize: Long
get() = try {
val dataDir = File("/data/data")
val appDir = File("/data/app")
calculateDirectorySize(dataDir) + calculateDirectorySize(appDir)
} catch (e: Exception) {
0L
}
// 系统占用大小
val systemSize: Long
get() = StatFs(Environment.getRootDirectory().absolutePath).let { statFs ->
statFs.blockCountLong * statFs.blockSizeLong
}
// 外部存储总空间
val externalStorageTotalSpace: Long?
get() = runCatching {
StatFs(Environment.getExternalStorageDirectory().absolutePath).let { statFs ->
@ -43,6 +101,7 @@ class StorageInfo(private val context: Context) {
}
}.getOrNull()
// 外部存储可用空间
val externalStorageAvailableSpace: Long?
get() = runCatching {
StatFs(Environment.getExternalStorageDirectory().absolutePath).let { statFs ->
@ -50,6 +109,7 @@ class StorageInfo(private val context: Context) {
}
}.getOrNull()
// 外部存储已使用空间
val externalStorageUsedSpace: Long?
get() = externalStorageTotalSpace?.let { total ->
externalStorageAvailableSpace?.let { available ->
@ -57,36 +117,76 @@ class StorageInfo(private val context: Context) {
}
}
// 外部存储是否为模拟存储
val isExternalStorageEmulated: Boolean
get() = Environment.isExternalStorageEmulated()
// 外部存储是否可移除
val isExternalStorageRemovable: Boolean
get() = Environment.isExternalStorageRemovable()
// 是否支持可更新的 APEX
val hasUpdatableApex: Boolean?
get() = SystemProperties.getBoolean("ro.apex.updatable")
// 是否使用 system-as-root 分区方案
val usesSystemAsRoot: Boolean?
get() = SystemProperties.getBoolean("ro.build.system_root_image")
// 是否使用 A/B 分区更新方案
val usesAb: Boolean?
get() = SystemProperties.getBoolean("ro.build.ab_update")
// A/B OTA 分区列表
val abOtaPartitions: Array<String>?
get() = SystemProperties.getString("ro.product.ab_ota_partitions")?.split(",")?.toTypedArray()
// 是否使用动态分区
val usesDynamicPartitions: Boolean?
get() = SystemProperties.getBoolean("ro.boot.dynamic_partitions")
// 是否使用改造的动态分区
val usesRetrofittedDynamicPartitions: Boolean?
get() = SystemProperties.getBoolean("ro.boot.dynamic_partitions_retrofit")
// 是否使用虚拟 A/B 分区
val usesVirtualAb: Boolean?
get() = SystemProperties.getBoolean("ro.virtual_ab.enabled")
// 是否使用改造的虚拟 A/B 分区
val usesRetrofittedVirtualAb: Boolean?
get() = SystemProperties.getBoolean("ro.virtual_ab.retrofit")
// 虚拟 A/B 分区是否启用压缩
val usesCompressedVirtualAb: Boolean?
get() = SystemProperties.getBoolean("ro.virtual_ab.compression.enabled")
// 计算目录大小(递归)
private fun calculateDirectorySize(directory: File): Long {
var size = 0L
try {
if (directory.exists() && directory.isDirectory) {
directory.listFiles()?.forEach { file ->
size += if (file.isDirectory) {
calculateDirectorySize(file)
} else {
file.length()
}
}
}
} catch (e: Exception) {
// 可能因权限问题无法访问某些目录
}
return size
}
// 格式化存储大小
fun formatBytes(bytes: Long): String {
return when {
bytes < 1024 -> "$bytes B"
bytes < 1024 * 1024 -> String.format("%.2f KB", bytes / 1024.0)
bytes < 1024 * 1024 * 1024 -> String.format("%.2f MB", bytes / (1024.0 * 1024))
else -> String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024))
}
}
}

View File

@ -0,0 +1,112 @@
package com.xyzshell.andinfo.utils
import android.util.Base64
import com.google.gson.Gson
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.IOException
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
class WebService {
private val client = OkHttpClient()
val gson = Gson()
// AES 密钥 (必须是 16/24/32 字节)
private val aesKey = "e67cbcee5e573d1b"
// AES 加密
fun encrypt(plainText: String): String {
return try {
val secretKey = SecretKeySpec(aesKey.toByteArray(), "AES")
val cipher = Cipher.getInstance("AES")
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
val encryptedBytes = cipher.doFinal(plainText.toByteArray())
Base64.encodeToString(encryptedBytes, Base64.NO_WRAP)
} catch (e: Exception) {
throw Exception("加密失败: ${e.message}")
}
}
fun postData(url: String, jsonData: String, callback: (String?, Exception?) -> Unit) {
val mediaType = "application/json; charset=utf-8".toMediaType()
val body = jsonData.toRequestBody(mediaType)
val request = Request.Builder()
.url(url)
.post(body)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
callback(null, e)
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (it.isSuccessful) {
callback(it.body?.string(), null)
} else {
callback(null, Exception("HTTP ${it.code}"))
}
}
}
})
}
// 同步版本
fun postDataSync(url: String, jsonData: String): String? {
val mediaType = "application/json; charset=utf-8".toMediaType()
val body = jsonData.toRequestBody(mediaType)
val request = Request.Builder()
.url(url)
.post(body)
.build()
client.newCall(request).execute().use { response ->
return if (response.isSuccessful) {
response.body?.string()
} else {
null
}
}
}
// 序列化对象为 JSON
fun <T> toJson(obj: T): String {
return gson.toJson(obj)
}
// 反序列化 JSON 为对象
inline fun <reified T> fromJson(json: String): T? {
return try {
gson.fromJson(json, T::class.java)
} catch (e: Exception) {
null
}
}
// 带序列化的 POST 请求
fun <T> postObject(url: String, obj: T, callback: (String?, Exception?) -> Unit) {
val jsonData = toJson(obj)
postData(url, jsonData, callback)
}
// 带序列化和反序列化的 POST 请求
inline fun <reified T, reified R> postAndParse(
url: String,
obj: T,
crossinline callback: (R?, Exception?) -> Unit
) {
val jsonData = toJson(obj)
postData(url, jsonData) { response, error ->
if (error != null) {
callback(null, error)
} else if (response != null) {
val result = fromJson<R>(response)
callback(result, null)
} else {
callback(null, Exception("Empty response"))
}
}
}
}