package com.example.modifier import android.accessibilityservice.AccessibilityServiceInfo import android.content.Context import android.content.pm.PackageManager import android.text.TextUtils import android.util.Log import android.view.accessibility.AccessibilityManager import androidx.core.app.ActivityCompat import com.example.modifier.http.ktorClient import com.example.modifier.service.ModifierService import io.ktor.client.request.head import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.time.ZoneId import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.util.Locale private const val TAG = "Modifier" suspend fun shellRun(vararg commands: String): Pair { var output = "" var error = "" Log.i(TAG, "shellRun: \t${commands.joinToString("\n\t\t\t")}") withContext(Dispatchers.IO) { val p = ProcessBuilder("su", "-M").start() p.outputStream.bufferedWriter().use { writer -> commands.forEach { command -> writer.write(command) writer.newLine() writer.flush() } } coroutineScope { launch { p.inputStream.bufferedReader().useLines { it.forEach { output += it + "\n" Log.i(TAG, "shellRunOut: $it") } } } launch { p.errorStream.bufferedReader().useLines { it.forEach { error += it + "\n" Log.e(TAG, "shellRunErr: $it") } } } } p.waitFor() } return Pair(output, error) } fun getContext(): Context? { try { val activityThreadClass = Class.forName("android.app.ActivityThread") val currentActivityThreadMethod = activityThreadClass.getMethod("currentActivityThread") currentActivityThreadMethod.isAccessible = true val currentActivityThread = currentActivityThreadMethod.invoke(null) val getApplicationMethod = activityThreadClass.getMethod("getApplication") getApplicationMethod.isAccessible = true return getApplicationMethod.invoke(currentActivityThread) as Context } catch (e: java.lang.Exception) { e.printStackTrace() } return null } suspend fun hasRootAccess(): Boolean { var rootAccess = false try { val (output, _) = shellRun("echo \"imrooted\"") if (output.contains("imrooted")) { rootAccess = true } } catch (e: Exception) { e.printStackTrace() } return rootAccess } fun hasPermission(permission: String): Boolean { return ActivityCompat.checkSelfPermission( getContext()!!, permission ) == PackageManager.PERMISSION_GRANTED } fun isAccessibilityEnabled(): Boolean { val context = getContext()!! val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager val enabledServices = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK) for (enabledService in enabledServices) { Log.i( TAG, "Enabled service: " + enabledService.resolveInfo.serviceInfo.packageName + "/" + enabledService.resolveInfo.serviceInfo.name ) val enabledServiceInfo = enabledService.resolveInfo.serviceInfo if (enabledServiceInfo.packageName == context.packageName && enabledServiceInfo.name == ModifierService.NAME) return true } return false } suspend fun enableAccessibility(): Boolean { if (isAccessibilityEnabled()) return true val context = getContext()!! val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager val enabledServices = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK) val names: MutableList = ArrayList() for (enabledService in enabledServices) { names.add(enabledService.resolveInfo.serviceInfo.packageName + "/" + enabledService.resolveInfo.serviceInfo.name) } names.add(context.packageName + "/" + ModifierService.NAME) try { shellRun( "settings put secure enabled_accessibility_services " + TextUtils.join(":", names), "settings put secure accessibility_enabled 1" ) return true } catch (e: java.lang.Exception) { e.printStackTrace() } return false } suspend fun enableOverlay() { try { shellRun("appops set " + BuildConfig.APPLICATION_ID + " SYSTEM_ALERT_WINDOW allow") } catch (e: java.lang.Exception) { e.printStackTrace() } } suspend fun syncTime() { try { Log.i("Modifier", "syncTime: start") val response = ktorClient("").head("http://www.baidu.com") val dateHeader = response.headers["Date"] val date = ZonedDateTime.parse( dateHeader, DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH) ) // convert to Asia/Shanghai val dateInZone = date.withZoneSameInstant(ZoneId.of("Asia/Shanghai")) Log.i( TAG, "CurrentTime from Baidu: ${dateInZone.format(DateTimeFormatter.ISO_DATE_TIME)}" ) shellRun( "settings put system time_12_24 24", "settings put global auto_time 0", "settings put global auto_time_zone 0", "setprop persist.sys.timezone Asia/Shanghai", "date \"${dateInZone.format(DateTimeFormatter.ofPattern("MMddHHmmyyyy.ss"))}\"" ) } catch (e: Exception) { Log.e(TAG, "Error SyncTime", e) } }