Android 获取设备相关信息

devtools/2024/12/22 13:53:19/

在做某些项目时,需要上传必要的设备信息给服务器用于风控

在此做一下总结。

一.获取的数据

主要包含下面数据

设备id
操作系统版本
设备型号
IMSI
路由器mac地址
设备mac地址
安卓id
经度
纬度
ip地址
谷歌广告id
手机可用内存
apk可用内存
apk可分配的最大内存
手机总内存
cpu主频
屏幕解析度
网络类型
是否调用usb
是否开启模拟器
电池电量
是否root过
设备总存储
设备可用存储
设备uuid
设备imei
sim卡槽状态

二.具体实现代码 

 DeviceUtils.kt

package com.uz.cashloanuzi.grabimport android.Manifest.permission
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.content.res.Resources
import android.hardware.usb.UsbManager
import android.location.LocationManager
import android.net.Uri
import android.net.wifi.WifiManager
import android.os.Build
import android.os.StatFs
import android.provider.Settings
import android.telephony.TelephonyManager
import android.text.TextUtils
import androidx.annotation.RequiresPermission
import androidx.core.app.ActivityCompat
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import com.uz.cashloanuzi.base.AppConstant
import com.uz.cashloanuzi.base.BaseApplication.Companion.application
import com.uz.cashloanuzi.grab.ShellUtils.CommandResult
import com.uz.cashloanuzi.utils.LogUtils
import com.uz.cashloanuzi.utils.SPUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.BufferedReader
import java.io.File
import java.io.FileReader
import java.io.IOException
import java.io.InputStreamReader
import java.math.BigDecimal
import java.math.RoundingMode
import java.net.InetAddress
import java.net.NetworkInterface
import java.net.SocketException
import java.util.Locale
import java.util.UUIDclass DeviceUtils private constructor() {companion object {val isDeviceRooted: Booleanget() {val su = "su"val locations = arrayOf("/system/bin/","/system/xbin/","/sbin/","/system/sd/xbin/","/system/bin/failsafe/","/data/local/xbin/","/data/local/bin/","/data/local/","/system/sbin/","/usr/bin/","/vendor/bin/")for (location in locations) {if (File(location + su).exists()) {return true}}return false}val sDKVersionName: Stringget() = Build.VERSION.RELEASEval sDKVersionCode: Intget() = Build.VERSION.SDK_INT@get:SuppressLint("HardwareIds")val androidID: Stringget() {val id = Settings.Secure.getString(application?.contentResolver,Settings.Secure.ANDROID_ID)if ("9774d56d682e549c" == id) return ""return id ?: ""}@get:RequiresPermission(allOf = [permission.ACCESS_WIFI_STATE, permission.CHANGE_WIFI_STATE])val macAddress: Stringget() {val macAddress = getMacAddress(*(null as Array<String?>?)!!)if (!TextUtils.isEmpty(macAddress) || wifiEnabled) return macAddresswifiEnabled = truewifiEnabled = falsereturn getMacAddress(*(null as Array<String?>?)!!)}@set:RequiresPermission(permission.CHANGE_WIFI_STATE)private var wifiEnabled: Booleanget() {@SuppressLint("WifiManagerLeak") val manager = application?.getSystemService(Context.WIFI_SERVICE) as WifiManager?: return falsereturn manager.isWifiEnabled}/*** Enable or disable wifi.** Must hold `<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />`** @param enabled True to enabled, false otherwise.*/private set(enabled) {@SuppressLint("WifiManagerLeak") val manager =application?.getSystemService(Context.WIFI_SERVICE) as WifiManager?: returnif (enabled == manager.isWifiEnabled) returnmanager.setWifiEnabled(enabled)}/*** Return the MAC address.** Must hold `<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />`,* `<uses-permission android:name="android.permission.INTERNET" />`** @return the MAC address*/@RequiresPermission(allOf = [permission.ACCESS_WIFI_STATE])fun getMacAddress(vararg excepts: String?): String {var macAddress = macAddressByNetworkInterfaceif (isAddressNotInExcepts(macAddress, excepts)) {return macAddress}macAddress = macAddressByInetAddressif (isAddressNotInExcepts(macAddress, excepts)) {return macAddress}macAddress = macAddressByWifiInfoif (isAddressNotInExcepts(macAddress, excepts)) {return macAddress}macAddress = getMacAddressByFile()if (isAddressNotInExcepts(macAddress, excepts)) {return macAddress}return ""}private fun isAddressNotInExcepts(address: String,vararg excepts: Array<out String?>): Boolean {if (TextUtils.isEmpty(address)) {return false}if ("02:00:00:00:00:00" == address) {return false}for (filter in excepts) {if (filter.toString() == address) {return false}}return true}@get:RequiresPermission(permission.ACCESS_WIFI_STATE)private val macAddressByWifiInfo: Stringget() {try {val wifi = application?.getApplicationContext()?.getSystemService(Context.WIFI_SERVICE) as WifiManagerif (wifi != null) {val info = wifi.connectionInfoif (info != null) {@SuppressLint("HardwareIds") val macAddress = info.macAddressif (!TextUtils.isEmpty(macAddress)) {return macAddress}}}} catch (e: Exception) {e.printStackTrace()}return "02:00:00:00:00:00"}private val macAddressByNetworkInterface: Stringget() {try {val nis = NetworkInterface.getNetworkInterfaces()while (nis.hasMoreElements()) {val ni = nis.nextElement()if (ni == null || !ni.name.equals("wlan0", ignoreCase = true)) continueval macBytes = ni.hardwareAddressif (macBytes != null && macBytes.size > 0) {val sb = StringBuilder()for (b in macBytes) {sb.append(String.format("%02x:", b))}return sb.substring(0, sb.length - 1)}}} catch (e: Exception) {e.printStackTrace()}return "02:00:00:00:00:00"}private val macAddressByInetAddress: Stringget() {try {val inetAddress = inetAddressif (inetAddress != null) {val ni = NetworkInterface.getByInetAddress(inetAddress)if (ni != null) {val macBytes = ni.hardwareAddressif (macBytes != null && macBytes.size > 0) {val sb = StringBuilder()for (b in macBytes) {sb.append(String.format("%02x:", b))}return sb.substring(0, sb.length - 1)}}}} catch (e: Exception) {e.printStackTrace()}return "02:00:00:00:00:00"}private val inetAddress: InetAddress?get() {try {val nis = NetworkInterface.getNetworkInterfaces()while (nis.hasMoreElements()) {val ni = nis.nextElement()// To prevent phone of xiaomi return "10.0.2.15"if (!ni.isUp) continueval addresses = ni.inetAddresseswhile (addresses.hasMoreElements()) {val inetAddress = addresses.nextElement()if (!inetAddress.isLoopbackAddress) {val hostAddress = inetAddress.hostAddressif (hostAddress.indexOf(':') < 0) return inetAddress}}}} catch (e: SocketException) {e.printStackTrace()}return null}@Suppress("DEPRECATED_IDENTITY_EQUALS")private fun getMacAddressByFile(): String{var result: ShellUtils.CommandResult =execCmd("getprop wifi.interface", false)if (result.result === 0) {val name: String = result.successMsgresult = execCmd("cat /sys/class/net/$name/address", false)if (result.result === 0) {val address: String = result.successMsgif (address.isNotEmpty()) {return address}}}return "02:00:00:00:00:00"}fun execCmd(command: String?, isRooted: Boolean): CommandResult {return ShellUtils.execCmd(command, isRooted)}val manufacturer: String/*** Return the manufacturer of the product/hardware.** e.g. Xiaomi** @return the manufacturer of the product/hardware*/get() = Build.MANUFACTURERval model: Stringget() {var model = Build.MODELmodel = model?.trim { it <= ' ' }?.replace("\\s*".toRegex(), "") ?: ""return model}val aBIs: Array<String>/*** Return an ordered list of ABIs supported by this device. The most preferred ABI is the first* element in the list.** @return an ordered list of ABIs supported by this device*/get() {return Build.SUPPORTED_ABIS}val isTablet: Boolean/*** Return whether device is tablet.** @return `true`: yes<br></br>`false`: no*/get() = ((Resources.getSystem().configuration.screenLayoutand Configuration.SCREENLAYOUT_SIZE_MASK)>= Configuration.SCREENLAYOUT_SIZE_LARGE)val isEmulator: Boolean/*** Return whether device is emulator.** @return `true`: yes<br></br>`false`: no*/get() {val checkProperty = (Build.FINGERPRINT.startsWith("generic")|| Build.FINGERPRINT.lowercase(Locale.getDefault()).contains("vbox")|| Build.FINGERPRINT.lowercase(Locale.getDefault()).contains("test-keys")|| 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" == Build.PRODUCTif (checkProperty) return truevar operatorName = ""val tm =application?.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManagerif (tm != null) {val name = tm.networkOperatorNameif (name != null) {operatorName = name}}val checkOperatorName = operatorName.equals("android", ignoreCase = true)if (checkOperatorName) return trueval url = "tel:" + "123456"val intent = Intent()intent.setData(Uri.parse(url))intent.setAction(Intent.ACTION_DIAL)val checkDial = intent.resolveActivity(application!!.packageManager) == nullif (checkDial) return trueif (isEmulatorByCpu) return truereturn false}private val isEmulatorByCpu: Boolean/*** Returns whether is emulator by check cpu info.* by function of [.readCpuInfo], obtain the device cpu information.* then compare whether it is intel or amd (because intel and amd are generally not mobile phone cpu), to determine whether it is a real mobile phone** @return `true`: yes<br></br>`false`: no*/get() {val cpuInfo = readCpuInfo()return cpuInfo.contains("intel") || cpuInfo.contains("amd")}/*** Return Cpu information** @return Cpu info*/private fun readCpuInfo(): String {var result = ""try {val args = arrayOf("/system/bin/cat", "/proc/cpuinfo")val cmd = ProcessBuilder(*args)val process = cmd.start()val sb = StringBuilder()var readLine: String?val responseReader = BufferedReader(InputStreamReader(process.inputStream, "utf-8"))while ((responseReader.readLine().also { readLine = it }) != null) {sb.append(readLine)}responseReader.close()result = sb.toString().lowercase(Locale.getDefault())} catch (ignored: IOException) {}return result}val isDevelopmentSettingsEnabled: Boolean/*** Whether user has enabled development settings.** @return whether user has enabled development settings.*/get() = Settings.Global.getInt(application?.contentResolver,Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) > 0private const val KEY_UDID = "KEY_UDID"@Volatileprivate var udid: String? = nullval uniqueDeviceId: String?/*** Return the unique device id.* <pre>{1}{UUID(macAddress)}</pre>* <pre>{2}{UUID(androidId )}</pre>* <pre>{9}{UUID(random    )}</pre>** @return the unique device id*/get() = getUniqueDeviceId("", true)/*** Return the unique device id.* <pre>android 10 deprecated {prefix}{1}{UUID(macAddress)}</pre>* <pre>{prefix}{2}{UUID(androidId )}</pre>* <pre>{prefix}{9}{UUID(random    )}</pre>** @param prefix The prefix of the unique device id.* @return the unique device id*/fun getUniqueDeviceId(prefix: String): String? {return getUniqueDeviceId(prefix, true)}/*** Return the unique device id.* <pre>{1}{UUID(macAddress)}</pre>* <pre>{2}{UUID(androidId )}</pre>* <pre>{9}{UUID(random    )}</pre>** @param useCache True to use cache, false otherwise.* @return the unique device id*/fun getUniqueDeviceId(useCache: Boolean): String? {return getUniqueDeviceId("", useCache)}/*** Return the unique device id.* <pre>android 10 deprecated {prefix}{1}{UUID(macAddress)}</pre>* <pre>{prefix}{2}{UUID(androidId )}</pre>* <pre>{prefix}{9}{UUID(random    )}</pre>** @param prefix   The prefix of the unique device id.* @param useCache True to use cache, false otherwise.* @return the unique device id*/fun getUniqueDeviceId(prefix: String, useCache: Boolean): String? {if (!useCache) {return getUniqueDeviceIdReal(prefix)}if (udid == null) {synchronized(DeviceUtils::class.java) {if (udid == null) {val id = SPUtils.getInstance().getString(KEY_UDID, null)if (id != null) {udid = idreturn udid}return getUniqueDeviceIdReal(prefix)}}}return udid}private fun getUniqueDeviceIdReal(prefix: String): String? {try {val androidId = androidIDif (!TextUtils.isEmpty(androidId)) {return saveUdid(prefix + 2, androidId)}} catch (ignore: Exception) { }return saveUdid(prefix + 9, "")}private fun saveUdid(prefix: String, id: String): String? {udid = getUdid(prefix, id)SPUtils.getInstance().put(KEY_UDID, udid)return udid}private fun getUdid(prefix: String, id: String): String {if (id.isEmpty()) {return prefix + UUID.randomUUID().toString().replace("-", "")}return prefix + UUID.nameUUIDFromBytes(id.toByteArray()).toString().replace("-", "")}@SuppressLint("MissingPermission", "HardwareIds")fun getDeviceIdentifier(context: Context): String {val telephonyManager =context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManagerreturn when {Build.VERSION.SDK_INT < Build.VERSION_CODES.O -> {telephonyManager.deviceId}Build.VERSION.SDK_INT in Build.VERSION_CODES.O until Build.VERSION_CODES.Q -> {telephonyManager.imei}else -> {Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)}}}@SuppressLint("MissingPermission", "HardwareIds")fun getIMSI(context: Context): String {val telephonyManager =context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManagerreturn if (ActivityCompat.checkSelfPermission(context,permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {androidID} else {try {telephonyManager.subscriberId ?: ""}catch (e:Exception){androidID}}} else {androidID}}fun isUSBConnected(context: Context): Boolean {val usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManagerval usbDevices = usbManager.deviceList// Check if any USB devices are connectedreturn usbDevices.isNotEmpty()}@SuppressLint("DefaultLocale")fun getLatitude(context: Context): BigDecimal? {// 获取位置(经纬度)(需要 LOCATION 权限)val locationManager =context.getSystemService(Context.LOCATION_SERVICE) as LocationManagerval location = if (ActivityCompat.checkSelfPermission(context,permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)} else {LogUtils.e("permission","permission.ACCESS_COARSE_LOCATION")null}val latitude = location?.latitudereturn if (latitude != null) {BigDecimal(latitude).setScale(5, RoundingMode.HALF_UP)} else {BigDecimal(AppConstant.DECIMAL_DEFAULT_VALUE)}}@SuppressLint("MissingPermission")fun getLongitude(context: Context): BigDecimal? {// 获取位置(经纬度)(需要 LOCATION 权限)val locationManager =context.getSystemService(Context.LOCATION_SERVICE) as LocationManagerval location = if (ActivityCompat.checkSelfPermission(context,permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)} else {LogUtils.e("permission","permission.ACCESS_COARSE_LOCATION")null}val longitude = location?.longitudereturn if (longitude != null) {BigDecimal(longitude).setScale(5, RoundingMode.HALF_UP)} else {BigDecimal(AppConstant.DECIMAL_DEFAULT_VALUE)}}fun getIpAddress(context: Context): String {val wifiManager =context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManagerval wifiInfo = wifiManager.connectionInforeturn android.text.format.Formatter.formatIpAddress(wifiInfo.ipAddress)}suspend fun getGaid(context: Context): String? {return withContext(Dispatchers.IO) {try {val advertisingIdInfo = AdvertisingIdClient.getAdvertisingIdInfo(context)advertisingIdInfo.id} catch (e: Exception) {e.printStackTrace()null}}}fun getPhoneAvailableMemory(): Double {// 手机可用内存val runtime = Runtime.getRuntime()return (runtime.freeMemory() / 1024 / 1024).toDouble()}fun getApkAvailableMemory(): Double {// APK 可用内存val runtime = Runtime.getRuntime()return (runtime.totalMemory() / 1024 / 1024).toDouble()}fun getApkMaxMemory(): Double {val runtime = Runtime.getRuntime()// APK 可分配的最大内存return (runtime.maxMemory() / 1024 / 1024).toDouble()}fun getSimState(context: Context): Int {val telephonyManager =context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManagerval simState = telephonyManager.simStatereturn simState}fun getTotalMemory(): Double {val totalMemory = BufferedReader(FileReader("/proc/meminfo")).useLines { lines ->lines.firstOrNull { it.startsWith("MemTotal:") }?.replace("\\D+".toRegex(), "")?.toLong()?.div(1024)}return totalMemory?.toDouble() ?: 0.0}fun getCpuFrequency(): Long {return BufferedReader(FileReader("/proc/cpuinfo")).useLines { lines ->lines.firstOrNull { it.startsWith("BogoMIPS") }?.replace("\\D+".toRegex(), "")?.toLong()} ?: 0}fun getScreenResolution(context: Context): String {val metrics = context.resources.displayMetricsreturn "${metrics.widthPixels}x${metrics.heightPixels}"}fun getBatteryPercent(context: Context): Int {return context.registerReceiver(null,android.content.IntentFilter(android.content.Intent.ACTION_BATTERY_CHANGED))?.getIntExtra(android.os.BatteryManager.EXTRA_LEVEL, -1) ?: -1}//磁盘fun getRomTotalSpace(context: Context): Double {val stat = StatFs(context.filesDir.path)val totalStorage = stat.totalBytes / 1024 / 1024 // in MBreturn totalStorage.toDouble()}//可用磁盘fun getAvailableRomStorage(context: Context): Double {val stat = StatFs(context.filesDir.path)// 设备可用存储val availableStorage = stat.availableBytes / 1024 / 1024 // in MBreturn availableStorage.toDouble()}//路由器地址fun getRouterMacAddress(context: Context): String? {val wifiManager =context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManagerval wifiInfo = wifiManager.connectionInforeturn if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {// Starting from Android 9 (Pie), this method returns "02:00:00:00:00:00" for privacy reasonswifiInfo.bssid} else {// On Android 8.1 and lower, the BSSID (MAC address of the access point) is available directlywifiInfo.bssid}}fun getNetworkType(context: Context): Int {val telephonyManager =context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManagerreturn if (ActivityCompat.checkSelfPermission(context,permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {LogUtils.e("permission","permission.READ_PHONE_STATE")return -1} elsetelephonyManager.networkType}suspend fun getDeviceInfo(context: Context): Map<String, Any> {val map: MutableMap<String, Any> = HashMap()map["deviceId"] = getUniqueDeviceId(true) ?: ""map["os"] = Build.VERSION.RELEASEmap["imsi"] = getIMSI(context)map["deviceMac"] = getMacAddress("")map["routerMac"] = getRouterMacAddress(context) ?: ""map["androidId"] = androidIDmap["lng"] = getLongitude(context)?:AppConstant.DECIMAL_DEFAULT_VALUEmap["lat"] = getLatitude(context)?:AppConstant.DECIMAL_DEFAULT_VALUEmap["ip"] = getIpAddress(context)map["googleAdvertisingId"] = getGaid(context) ?: ""map["cpu_speed"] = getCpuFrequency()map["screenResolution"] = getScreenResolution(context)//分辨率map["emulator"] = isEmulator //模拟器map["batterypercent"] = getBatteryPercent(context)//模拟器map["romTotalSpace"] = getRomTotalSpace(context)//磁盘总大小map["romFreeSpace"] = getAvailableRomStorage(context)//可用磁盘大小map["uuid"] = UUID.randomUUID().toString()//UUIDmap["simState"] = getSimState(context)//simStatemap["mobilePhoneModel"] = model//设备型号map["ramTotalSpace"] = getTotalMemory()//内存总大小map["romFreeSpace"] = getPhoneAvailableMemory()//手机可用内存map["runtime_available_memory"] = getApkAvailableMemory()//apk可用内存map["runtime_max_memory"] = getApkMaxMemory()//apk可用最大内存map["network"] = getNetworkType(context).toString()map["usb_enabled"] = isUSBConnected(context)map["root"] = isDeviceRootedmap["imei"] = getUniqueDeviceId(true) ?: ""LogUtils.e("imei 11111111", getUniqueDeviceId(true))return map}}
}

ShellUtils.kt(mac地址需要)

package com.uz.cashloanuzi.grab;import androidx.annotation.NonNull;import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;public final class ShellUtils {private static final String LINE_SEP = System.getProperty("line.separator");private ShellUtils() {throw new UnsupportedOperationException("u can't instantiate me...");}public static CommandResult execCmd(final String command, final boolean isRooted) {return execCmd(new String[]{command}, isRooted, true);}/*** Execute the command.** @param commands        The commands.* @param isRooted        True to use root, false otherwise.* @param isNeedResultMsg True to return the message of result, false otherwise.* @return the single {@link CommandResult} instance*/public static CommandResult execCmd(final String[] commands,final boolean isRooted,final boolean isNeedResultMsg) {return execCmd(commands, null, isRooted, isNeedResultMsg);}/*** Execute the command.** @param commands        The commands.* @param envp            Array of strings, each element of which*                        has environment variable settings in the format*                        <i>name</i>=<i>value</i>, or*                        <tt>null</tt> if the subprocess should inherit*                        the environment of the current process.* @param isRooted        True to use root, false otherwise.* @param isNeedResultMsg True to return the message of result, false otherwise.* @return the single {@link CommandResult} instance*/public static CommandResult execCmd(final String[] commands,final String[] envp,final boolean isRooted,final boolean isNeedResultMsg) {int result = -1;if (commands == null || commands.length == 0) {return new CommandResult(result, "", "");}Process process = null;BufferedReader successResult = null;BufferedReader errorResult = null;StringBuilder successMsg = null;StringBuilder errorMsg = null;DataOutputStream os = null;try {process = Runtime.getRuntime().exec(isRooted ? "su" : "sh", envp, null);os = new DataOutputStream(process.getOutputStream());for (String command : commands) {if (command == null) continue;os.write(command.getBytes());os.writeBytes(LINE_SEP);os.flush();}os.writeBytes("exit" + LINE_SEP);os.flush();result = process.waitFor();if (isNeedResultMsg) {successMsg = new StringBuilder();errorMsg = new StringBuilder();successResult = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));String line;if ((line = successResult.readLine()) != null) {successMsg.append(line);while ((line = successResult.readLine()) != null) {successMsg.append(LINE_SEP).append(line);}}if ((line = errorResult.readLine()) != null) {errorMsg.append(line);while ((line = errorResult.readLine()) != null) {errorMsg.append(LINE_SEP).append(line);}}}} catch (Exception e) {e.printStackTrace();} finally {try {if (os != null) {os.close();}} catch (IOException e) {e.printStackTrace();}try {if (successResult != null) {successResult.close();}} catch (IOException e) {e.printStackTrace();}try {if (errorResult != null) {errorResult.close();}} catch (IOException e) {e.printStackTrace();}if (process != null) {process.destroy();}}return new CommandResult(result,successMsg == null ? "" : successMsg.toString(),errorMsg == null ? "" : errorMsg.toString());}/*** The result of command.*/public static class CommandResult {public int    result;public String successMsg;public String errorMsg;public CommandResult(final int result, final String successMsg, final String errorMsg) {this.result = result;this.successMsg = successMsg;this.errorMsg = errorMsg;}@Overridepublic String toString() {return "result: " + result + "\n" +"successMsg: " + successMsg + "\n" +"errorMsg: " + errorMsg;}}
}

三.一些备注

1.设备ID  deviceId 和 imei

关于设备ID,很久之前可以直接获取IMEI,后面因为Android迭代,这玩意儿现在都拿不到了。

因为需要敏感权限"android.permission.READ_PRIVILEGED_PHONE_STATE" 

这权限很多时候谷歌是不会允许普通应用拿到的。我现在返回的是AndroidID

    fun getDeviceIdentifier(context: Context): String {val telephonyManager =context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManagerreturn when {Build.VERSION.SDK_INT < Build.VERSION_CODES.O -> {telephonyManager.deviceId}Build.VERSION.SDK_INT in Build.VERSION_CODES.O until Build.VERSION_CODES.Q -> {telephonyManager.imei}else -> {Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)}}}
2.相关权限

这上面主要需要READ_PHONE_STATE和ACCESS_WIFI_STATE及ACCESS_COARSE_LOCATION这三个权限,需要在manifest进行声明哦,这三个权限在谷歌审核比较好过

3.备注

里面还有一些方法因为项目需求没有用到,所以我也没有删。

当然,风控有时候也不止需要这些数据,后面再进行补充吧,上面得方法里得具体数据是啥意思可以看我上面得注释。


http://www.ppmy.cn/devtools/103270.html

相关文章

【小白深度学习入门】【1】卷积神经网络CNN 结构、基本原理以及常见问题详解

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发…

十五分钟速通Vue

绑值语法( {{}} ) <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head…

用AI工具制作高质量PPT的完整教程

其实使用AI或者ChatGPT写PPT的方式有很多种 下面主要说明两种方式&#xff0c;建议第二种方式 接下来我会用最简单的语言&#xff0c;一步一步说明&#xff0c;保证你能在五分钟内做完你想要的PPT 具体方法和工具&#xff0c;都在里面 1、手动生成 让ChatGPT帮你生成大纲和…

NTP简介及相关概念

NTP简介及相关概念 记录NTP相关的一些知识点。 1. 什么是NTP 1.1 NTP简介 网络时间协议NTP&#xff08;Network Time Protocol&#xff09;是TCP/IP协议族里面的一个应用层协议&#xff0c;用来使客户端和服务器之间进行时钟同步&#xff0c;提供高精准度的时间校正。NTP服…

sqlmap注入语句学习,帮助你排查SQL注入漏洞

摘要 sqlmap是一个开源的渗透测试工具&#xff0c;可以用来进行自动化检测&#xff0c;利用SQL注入漏洞&#xff0c;获取数据库服务器的权限。它具有功能强大的检测引擎&#xff0c;针对各种不同类型数据库的渗透测试的功能选项&#xff0c;包括获取数据库中存储的数据&#x…

详解数据结构-栈的基本操作以及栈数组、栈链表的实现(C语言代码实现)

1. 栈的概念 在开始前&#xff0c;请牢记这句话&#xff1a;栈是一种先进后出的数据结构。 栈&#xff08;stack&#xff09;是限定仅在表的一端进行操作的数据结构&#xff0c;请联系我们前文所学的&#xff0c;设想一个单链表我们只能够对其链表的表尾结点进行操作&#xf…

GraphQL:API开发的未来,重塑数据交互的艺术

标题&#xff1a;GraphQL&#xff1a;API开发的未来&#xff0c;重塑数据交互的艺术 在当今快速发展的Web应用世界中&#xff0c;API&#xff08;应用程序编程接口&#xff09;已成为前后端分离架构的核心。然而&#xff0c;传统的RESTful API存在诸多限制&#xff0c;如过度获…

LeetCode_sql_day17(1843.可疑银行账户)

描述&#xff1a; 表&#xff1a;Accounts ---------------------- | Column Name | Type | ---------------------- | account_id | int | | max_income | int | ---------------------- account_id 是这张表具有唯一值的列。 每行包含一个银行账户每月最大收入的…