package com.shukria.kiosklauncher.util import android.Manifest import android.annotation.SuppressLint import android.content.Context import android.content.pm.PackageManager import android.net.TrafficStats import android.os.Build import android.provider.Settings import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.util.Log import androidx.core.content.ContextCompat object DeviceInfo { private const val TAG = "DeviceInfo" @SuppressLint("MissingPermission", "HardwareIds") fun getSerialNumber(context: Context): String { // Device Owner with READ_PHONE_STATE can call Build.getSerial() on Android 8+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { try { val serial = Build.getSerial() if (serial.isNotBlank() && serial != Build.UNKNOWN) { return serial } } catch (e: SecurityException) { Log.w(TAG, "Build.getSerial() denied, falling back to ANDROID_ID") } } else { @Suppress("DEPRECATION") val serial = Build.SERIAL if (serial.isNotBlank() && serial != Build.UNKNOWN) return serial } // Fallback: ANDROID_ID is stable per device + signing key return Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID) ?: "unknown-device" } fun getAppVersion(context: Context): String { return try { context.packageManager.getPackageInfo(context.packageName, 0).versionName ?: "1.0" } catch (e: Exception) { "1.0" } } @SuppressLint("MissingPermission") fun getSimDetails(context: Context): Map { val details = mutableMapOf() // 1. Try to read SIM details from MoreFun YSDK try { val devInfo = YSDKManager.getDevInfo() if (devInfo != null) { val imsi = devInfo.getString(com.morefun.yapi.engine.DeviceInfoConstrants.IMSI) val iccid = devInfo.getString(com.morefun.yapi.engine.DeviceInfoConstrants.ICCID) val imsi2 = devInfo.getString(com.morefun.yapi.engine.DeviceInfoConstrants.IMSI2) val iccid2 = devInfo.getString(com.morefun.yapi.engine.DeviceInfoConstrants.ICCID2) if (!imsi.isNullOrBlank()) details["imsi"] = imsi if (!iccid.isNullOrBlank()) details["iccid"] = iccid if (!imsi2.isNullOrBlank()) details["imsi2"] = imsi2 if (!iccid2.isNullOrBlank()) details["iccid2"] = iccid2 } } catch (e: Throwable) { Log.w(TAG, "YSDK device info call failed or class not found, falling back to standard telephony", e) } // 2. Fallback/Complemented with native Android APIs try { val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager if (!details.containsKey("operator") || details["operator"] == "UNKNOWN") { details["operator"] = telephonyManager.simOperatorName ?: "UNKNOWN" } if (!details.containsKey("sim_country") || details["sim_country"] == "UNKNOWN") { details["sim_country"] = telephonyManager.simCountryIso ?: "UNKNOWN" } if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { val subManager = context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager val infoList = subManager.activeSubscriptionInfoList if (!infoList.isNullOrEmpty()) { val mainSub = infoList[0] if (!details.containsKey("iccid")) { details["iccid"] = mainSub.iccId ?: "UNKNOWN" } val num = mainSub.number details["number"] = if (!num.isNullOrEmpty()) num else { try { telephonyManager.line1Number ?: "UNKNOWN" } catch (e: Exception) { "UNKNOWN" } } } else { if (!details.containsKey("iccid")) details["iccid"] = "UNKNOWN" details["number"] = telephonyManager.line1Number ?: "UNKNOWN" } } else { details["number"] = telephonyManager.line1Number ?: "UNKNOWN" if (!details.containsKey("iccid")) details["iccid"] = "UNKNOWN" } } else { if (!details.containsKey("number")) details["number"] = "PERMISSION_DENIED" if (!details.containsKey("iccid")) details["iccid"] = "PERMISSION_DENIED" } } catch (e: Exception) { Log.e(TAG, "Error getting SIM details via telephony", e) if (!details.containsKey("number")) details["number"] = "UNKNOWN" if (!details.containsKey("iccid")) details["iccid"] = "UNKNOWN" } return details } fun getTotalDataUsageMb(): String { return try { val rx = TrafficStats.getTotalRxBytes() val tx = TrafficStats.getTotalTxBytes() if (rx == TrafficStats.UNSUPPORTED.toLong() || tx == TrafficStats.UNSUPPORTED.toLong()) { "UNSUPPORTED" } else { val totalMb = (rx + tx) / (1024 * 1024) totalMb.toString() } } catch (e: Exception) { "UNKNOWN" } } fun getMobileDataUsageMb(): String { return try { val rx = TrafficStats.getMobileRxBytes() val tx = TrafficStats.getMobileTxBytes() if (rx == TrafficStats.UNSUPPORTED.toLong() || tx == TrafficStats.UNSUPPORTED.toLong()) { "UNSUPPORTED" } else { val totalMb = (rx + tx) / (1024 * 1024) totalMb.toString() } } catch (e: Exception) { "UNKNOWN" } } fun getInstalledApps(context: Context): String { return try { val pm = context.packageManager val apps = pm.getInstalledApplications(PackageManager.GET_META_DATA) val appList = mutableListOf() for (app in apps) { val label = try { app.loadLabel(pm).toString() } catch (e: Exception) { app.packageName } appList.add("$label (${app.packageName})") } appList.sort() appList.joinToString(", ") } catch (e: Exception) { "UNKNOWN" } } }