From e2800ffe8e8a9c826bde210adf6da849f24d0b50 Mon Sep 17 00:00:00 2001 From: Administrator Date: Sat, 12 Jul 2025 10:23:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E6=9C=BA=E5=8F=82=E6=95=B0=E6=96=B0?= =?UTF-8?q?=E5=A2=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 5 + .../java/com/android/grape/MainActivity.kt | 7 + .../java/com/android/grape/data/Device.kt | 32 +++- .../java/com/android/grape/data/Sensor.kt | 4 +- .../com/android/grape/job/AutoJobService.kt | 2 +- .../grape/provider/DeviceDataAccessor.kt | 100 ++++++++++++ .../grape/provider/DeviceInfoContract.kt | 27 ++++ .../grape/provider/DeviceInfoDbHelper.kt | 33 ++++ .../grape/provider/DeviceInfoHelper.kt | 8 + .../grape/provider/DeviceInfoProvider.kt | 148 ++++++++++++++++++ .../grape/util/ChangeDeviceInfoUtil.kt | 2 + .../java/com/android/grape/util/FileUtils.kt | 30 ++++ .../com/android/grape/util/ShellUtils.java | 6 +- .../main/java/com/android/grape/util/Util.kt | 6 +- 14 files changed, 401 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/android/grape/provider/DeviceDataAccessor.kt create mode 100644 app/src/main/java/com/android/grape/provider/DeviceInfoContract.kt create mode 100644 app/src/main/java/com/android/grape/provider/DeviceInfoDbHelper.kt create mode 100644 app/src/main/java/com/android/grape/provider/DeviceInfoHelper.kt create mode 100644 app/src/main/java/com/android/grape/provider/DeviceInfoProvider.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bca1633..3a5b8f4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,6 +48,11 @@ android:supportsRtl="true" android:theme="@style/Theme.AndroidGrape" tools:targetApi="31"> + + diff --git a/app/src/main/java/com/android/grape/MainActivity.kt b/app/src/main/java/com/android/grape/MainActivity.kt index 897496c..db6d8d5 100644 --- a/app/src/main/java/com/android/grape/MainActivity.kt +++ b/app/src/main/java/com/android/grape/MainActivity.kt @@ -13,6 +13,8 @@ import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import com.android.grape.databinding.ActivityMainBinding import com.android.grape.job.MonitorService +import com.android.grape.provider.DeviceDataAccessor +import com.android.grape.provider.DeviceInfoHelper import com.android.grape.util.ClashUtil import com.android.grape.util.MockTools import com.android.grape.util.NotificationPermissionHandler @@ -22,6 +24,7 @@ import com.android.grape.util.StoragePermissionHelper import com.android.grape.util.Util import com.android.grape.util.Util.AUTO_JSPACKAGENAME import com.android.grape.util.Util.killRecordProcess +import com.google.gson.Gson class MainActivity : AppCompatActivity() { private val viewModel by viewModels() @@ -42,9 +45,13 @@ class MainActivity : AppCompatActivity() { ScriptUtil.registerScriptResultReceiver() viewBinding.start.setOnClickListener { MonitorService.onEvent(MainApplication.instance) +// DeviceDataAccessor.saveDeviceInfo(this, DeviceInfoHelper.getDeviceId(this)) } viewBinding.stop.setOnClickListener { killRecordProcess(this, packageName) +// DeviceDataAccessor.deleteDeviceInfo(this, DeviceInfoHelper.getDeviceId(this)) +// val deviceInfo = DeviceDataAccessor.getDeviceInfo(this, DeviceInfoHelper.getDeviceId(this)) +// Log.d("TAG", "onCreate: ${Gson().toJson(deviceInfo?:"")}") } } diff --git a/app/src/main/java/com/android/grape/data/Device.kt b/app/src/main/java/com/android/grape/data/Device.kt index 0609d6a..70c9c56 100644 --- a/app/src/main/java/com/android/grape/data/Device.kt +++ b/app/src/main/java/com/android/grape/data/Device.kt @@ -2,6 +2,8 @@ package com.android.grape.data import com.google.gson.annotations.SerializedName +import org.json.JSONArray +import org.json.JSONObject data class Device( var advertiserId: String = "", @@ -110,4 +112,32 @@ data class Device( var vendingVersionName: String = "", @SerializedName("web_ua") var webUa: String = "" -) \ No newline at end of file +){ + fun toJson(): String { + return JSONObject().apply { + put("sensors", JSONArray(sensor)) + put("arch", arch) + put("cpu_abi", cpuAbi) + put("cpu_abi2", cpuAbi2) + put("size", dim.size) + put("xdp", dim.xdp) + put("ydp", dim.ydp) + put("y_px", dim.yPx) + put("x_px", dim.xPx) + put("d_dpi", dim.dDpi) + put("btch", btch) + put("btl", batteryLevel) + put("disk", disk) + put("sdk", sdk) + put("network", network) + put("api_ver", vendingVersionCode) + put("api_ver_name", vendingVersionName) + put("carrier", carrier) + put("product", product) + put("last_boot_time", lastBootTime) + put("install_source_info", installerPackage) + put("advertiserId", advertiserId) + }.toString() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/data/Sensor.kt b/app/src/main/java/com/android/grape/data/Sensor.kt index ae1b569..dca23a5 100644 --- a/app/src/main/java/com/android/grape/data/Sensor.kt +++ b/app/src/main/java/com/android/grape/data/Sensor.kt @@ -9,4 +9,6 @@ data class Sensor( var sV: String = "", var sVE: List = listOf(), var sVS: List = listOf() -) \ No newline at end of file +) { + companion object +} \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/job/AutoJobService.kt b/app/src/main/java/com/android/grape/job/AutoJobService.kt index 4c16914..f649ab5 100644 --- a/app/src/main/java/com/android/grape/job/AutoJobService.kt +++ b/app/src/main/java/com/android/grape/job/AutoJobService.kt @@ -15,7 +15,7 @@ class AutoJobService : JobIntentService() { Handler(Looper.getMainLooper()).postDelayed({ ScriptUtil.execScript( this@AutoJobService, - "/sdcard/autojs/main.js" + "/sdcard/script/main.js" ) }, 2000L) } else { diff --git a/app/src/main/java/com/android/grape/provider/DeviceDataAccessor.kt b/app/src/main/java/com/android/grape/provider/DeviceDataAccessor.kt new file mode 100644 index 0000000..697c943 --- /dev/null +++ b/app/src/main/java/com/android/grape/provider/DeviceDataAccessor.kt @@ -0,0 +1,100 @@ +package com.android.grape.provider + +import android.content.ContentValues +import android.content.Context +import android.net.Uri +import com.android.grape.data.Device + +// DeviceDataAccessor.kt +object DeviceDataAccessor { + + // 保存设备信息到提供方 + fun saveDeviceInfo(context: Context, device: Device): Uri? { + val resolver = context.contentResolver + val actualDeviceId = DeviceInfoHelper.getDeviceId() + + val values = ContentValues().apply { + put(DeviceInfoContract.DeviceInfoEntry.COLUMN_DEVICE_ID, actualDeviceId) + put(DeviceInfoContract.DeviceInfoEntry.COLUMN_DATA, device.toJson()) + } + + // 尝试更新现有记录 + val updateCount = resolver.update( + getDeviceUri(actualDeviceId), + values, + null, + null + ) + + // 如果没有更新记录,则插入新记录 + return if (updateCount == 0) { + resolver.insert(DeviceInfoContract.CONTENT_URI, values) + } else { + getDeviceUri(actualDeviceId) + } + } + + // 读取设备信息 + fun getDeviceInfo(context: Context, deviceId: String? = null): String? { + val resolver = context.contentResolver + val actualDeviceId = deviceId ?: DeviceInfoHelper.getDeviceId() + + val cursor = resolver.query( + getDeviceUri(actualDeviceId), + arrayOf(DeviceInfoContract.DeviceInfoEntry.COLUMN_DATA), + null, + null, + null + ) + + return cursor?.use { + if (it.moveToFirst()) { + val json = + it.getString(it.run { it.getColumnIndex(DeviceInfoContract.DeviceInfoEntry.COLUMN_DATA) }) + json + } else { + null + } + } + } + + // 获取所有设备信息 + fun getAllDeviceInfo(context: Context): List { + val resolver = context.contentResolver + val deviceList = mutableListOf() + + resolver.query( + DeviceInfoContract.CONTENT_URI, + arrayOf(DeviceInfoContract.DeviceInfoEntry.COLUMN_DATA), + null, + null, + "${DeviceInfoContract.DeviceInfoEntry.COLUMN_TIMESTAMP} DESC" + )?.use { cursor -> + val dataIndex = cursor.getColumnIndex(DeviceInfoContract.DeviceInfoEntry.COLUMN_DATA) + + while (cursor.moveToNext()) { + val json = cursor.getString(dataIndex) + deviceList.add(json) + } + } + + return deviceList + } + + // 删除设备信息 + fun deleteDeviceInfo(context: Context, deviceId: String? = null): Int { + val actualDeviceId = deviceId ?: DeviceInfoHelper.getDeviceId() + return context.contentResolver.delete( + getDeviceUri(actualDeviceId), + null, + null + ) + } + + private fun getDeviceUri(deviceId: String): Uri { + return Uri.withAppendedPath( + DeviceInfoContract.CONTENT_URI, + "device/$deviceId" + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/provider/DeviceInfoContract.kt b/app/src/main/java/com/android/grape/provider/DeviceInfoContract.kt new file mode 100644 index 0000000..54074d9 --- /dev/null +++ b/app/src/main/java/com/android/grape/provider/DeviceInfoContract.kt @@ -0,0 +1,27 @@ +package com.android.grape.provider + +import android.net.Uri +import androidx.core.net.toUri + +object DeviceInfoContract { + const val AUTHORITY = "com.android.grape.deviceinfo.provider" + val CONTENT_URI: Uri = "content://$AUTHORITY/device_info".toUri() + + object DeviceInfoEntry { + const val TABLE_NAME = "device_info" + const val COLUMN_DEVICE_ID = "device_id" + const val COLUMN_DATA = "data" + const val COLUMN_TIMESTAMP = "timestamp" + + // JSON 键名 + const val KEY_CPU_ABI = "cpu_abi" + const val KEY_DIM = "dim" + const val KEY_BTCH = "btch" + const val KEY_ARCH = "arch" + const val KEY_BTL = "btl" + const val KEY_CPU_ABI2 = "cpu_abi2" + const val KEY_DISK = "disk" + const val KEY_SDK = "sdk" + const val KEY_NETWORK = "network" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/provider/DeviceInfoDbHelper.kt b/app/src/main/java/com/android/grape/provider/DeviceInfoDbHelper.kt new file mode 100644 index 0000000..9387501 --- /dev/null +++ b/app/src/main/java/com/android/grape/provider/DeviceInfoDbHelper.kt @@ -0,0 +1,33 @@ +package com.android.grape.provider + +import android.content.Context +import android.database.sqlite.SQLiteDatabase +import android.database.sqlite.SQLiteOpenHelper +import android.provider.BaseColumns + +class DeviceInfoDbHelper(context: Context) : SQLiteOpenHelper( + context, DATABASE_NAME, null, DATABASE_VERSION +) { + companion object { + const val DATABASE_NAME = "device_info.db" + const val DATABASE_VERSION = 1 + + const val SQL_CREATE_ENTRIES = """ + CREATE TABLE ${DeviceInfoContract.DeviceInfoEntry.TABLE_NAME} ( + ${BaseColumns._ID} INTEGER PRIMARY KEY, + ${DeviceInfoContract.DeviceInfoEntry.COLUMN_DEVICE_ID} TEXT UNIQUE, + ${DeviceInfoContract.DeviceInfoEntry.COLUMN_DATA} TEXT NOT NULL, + ${DeviceInfoContract.DeviceInfoEntry.COLUMN_TIMESTAMP} INTEGER NOT NULL DEFAULT (strftime('%s','now')) + ) + """ + } + + override fun onCreate(db: SQLiteDatabase) { + db.execSQL(SQL_CREATE_ENTRIES) + } + + override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("DROP TABLE IF EXISTS ${DeviceInfoContract.DeviceInfoEntry.TABLE_NAME}") + onCreate(db) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/provider/DeviceInfoHelper.kt b/app/src/main/java/com/android/grape/provider/DeviceInfoHelper.kt new file mode 100644 index 0000000..552593d --- /dev/null +++ b/app/src/main/java/com/android/grape/provider/DeviceInfoHelper.kt @@ -0,0 +1,8 @@ +package com.android.grape.provider + +object DeviceInfoHelper { + // 获取设备唯一ID + fun getDeviceId(): String { + return "d1b3e7f8c9a04b8e9f2c1d5e6f7a8b9c" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/provider/DeviceInfoProvider.kt b/app/src/main/java/com/android/grape/provider/DeviceInfoProvider.kt new file mode 100644 index 0000000..b14a5aa --- /dev/null +++ b/app/src/main/java/com/android/grape/provider/DeviceInfoProvider.kt @@ -0,0 +1,148 @@ +package com.android.grape.provider + +import android.content.ContentProvider +import android.content.ContentUris +import android.content.ContentValues +import android.content.UriMatcher +import android.database.Cursor +import android.database.sqlite.SQLiteQueryBuilder +import android.net.Uri +import android.provider.BaseColumns +import java.sql.SQLException + +// DeviceInfoProvider.kt +class DeviceInfoProvider : ContentProvider() { + private lateinit var dbHelper: DeviceInfoDbHelper + private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { + addURI(DeviceInfoContract.AUTHORITY, "device_info", 1) + addURI(DeviceInfoContract.AUTHORITY, "device_info/#", 2) + addURI(DeviceInfoContract.AUTHORITY, "device_info/device/*", 3) + } + + override fun onCreate(): Boolean { + dbHelper = DeviceInfoDbHelper(context!!) + return true + } + + override fun query( + uri: Uri, + projection: Array?, + selection: String?, + selectionArgs: Array?, + sortOrder: String? + ): Cursor? { + val db = dbHelper.readableDatabase + val qb = SQLiteQueryBuilder().apply { + tables = DeviceInfoContract.DeviceInfoEntry.TABLE_NAME + } + + when (uriMatcher.match(uri)) { + 1 -> {} // 查询所有 + 2 -> qb.appendWhere("${BaseColumns._ID} = ${uri.lastPathSegment}") + 3 -> qb.appendWhere("${DeviceInfoContract.DeviceInfoEntry.COLUMN_DEVICE_ID} = '${uri.lastPathSegment}'") + else -> throw IllegalArgumentException("Unknown URI: $uri") + } + + return qb.query(db, projection, selection, selectionArgs, null, null, sortOrder).apply { + setNotificationUri(context!!.contentResolver, uri) + } + } + + override fun insert(uri: Uri, values: ContentValues?): Uri? { + val db = dbHelper.writableDatabase + val now = System.currentTimeMillis() / 1000 + + // 添加时间戳 + values?.put(DeviceInfoContract.DeviceInfoEntry.COLUMN_TIMESTAMP, now) + + val rowId = db.insert(DeviceInfoContract.DeviceInfoEntry.TABLE_NAME, null, values) + + if (rowId > 0) { + val newUri = ContentUris.withAppendedId(DeviceInfoContract.CONTENT_URI, rowId) + context?.contentResolver?.notifyChange(newUri, null) + return newUri + } + return null + } + + override fun update( + uri: Uri, + values: ContentValues?, + selection: String?, + selectionArgs: Array? + ): Int { + val db = dbHelper.writableDatabase + val now = System.currentTimeMillis() / 1000 + + // 更新时间戳 + values?.put(DeviceInfoContract.DeviceInfoEntry.COLUMN_TIMESTAMP, now) + + val count = when (uriMatcher.match(uri)) { + 1 -> db.update( + DeviceInfoContract.DeviceInfoEntry.TABLE_NAME, + values, selection, selectionArgs + ) + 2 -> { + val id = uri.lastPathSegment + db.update( + DeviceInfoContract.DeviceInfoEntry.TABLE_NAME, + values, + "${BaseColumns._ID} = ?", + arrayOf(id) + ) + } + 3 -> { + val deviceId = uri.lastPathSegment + db.update( + DeviceInfoContract.DeviceInfoEntry.TABLE_NAME, + values, + "${DeviceInfoContract.DeviceInfoEntry.COLUMN_DEVICE_ID} = ?", + arrayOf(deviceId) + ) + } + else -> throw IllegalArgumentException("Unknown URI: $uri") + } + + context?.contentResolver?.notifyChange(uri, null) + return count + } + + override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int { + val db = dbHelper.writableDatabase + + val count = when (uriMatcher.match(uri)) { + 1 -> db.delete( + DeviceInfoContract.DeviceInfoEntry.TABLE_NAME, + selection, selectionArgs + ) + 2 -> { + val id = uri.lastPathSegment + db.delete( + DeviceInfoContract.DeviceInfoEntry.TABLE_NAME, + "${BaseColumns._ID} = ?", + arrayOf(id) + ) + } + 3 -> { + val deviceId = uri.lastPathSegment + db.delete( + DeviceInfoContract.DeviceInfoEntry.TABLE_NAME, + "${DeviceInfoContract.DeviceInfoEntry.COLUMN_DEVICE_ID} = ?", + arrayOf(deviceId) + ) + } + else -> throw IllegalArgumentException("Unknown URI: $uri") + } + + context?.contentResolver?.notifyChange(uri, null) + return count + } + + override fun getType(uri: Uri): String? { + return when (uriMatcher.match(uri)) { + 1 -> "${DeviceInfoContract.AUTHORITY}/device_info" + 2, 3 -> "vnd.android.cursor.item/vnd.example.deviceinfo" + else -> throw IllegalArgumentException("Unknown URI: $uri") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/util/ChangeDeviceInfoUtil.kt b/app/src/main/java/com/android/grape/util/ChangeDeviceInfoUtil.kt index d9f6a06..c96fc44 100644 --- a/app/src/main/java/com/android/grape/util/ChangeDeviceInfoUtil.kt +++ b/app/src/main/java/com/android/grape/util/ChangeDeviceInfoUtil.kt @@ -7,6 +7,7 @@ import com.android.grape.MainApplication import com.android.grape.data.Device import com.android.grape.net.Api import com.android.grape.net.ChangeCallBack +import com.android.grape.provider.DeviceDataAccessor import com.android.grape.util.Util.paramsJson import com.blankj.utilcode.util.LogUtils import kotlinx.coroutines.CoroutineScope @@ -28,6 +29,7 @@ object ChangeDeviceInfoUtil { return } val device = GsonUtils.fromJsonObject(deviceObject.toString(), Device::class.java) + DeviceDataAccessor.saveDeviceInfo(MainApplication.instance, device) val padCode = ShellUtils.execRootCmdAndGetResult("getprop ro.boot.pad_code") Log.d("TAG", "changeDevice: $padCode") val jsonString = JSONObject().apply { diff --git a/app/src/main/java/com/android/grape/util/FileUtils.kt b/app/src/main/java/com/android/grape/util/FileUtils.kt index a266b3b..81ad933 100644 --- a/app/src/main/java/com/android/grape/util/FileUtils.kt +++ b/app/src/main/java/com/android/grape/util/FileUtils.kt @@ -1,6 +1,8 @@ package com.android.grape.util +import android.os.Environment import android.util.Log +import com.blankj.utilcode.util.LogUtils import java.io.BufferedInputStream import java.io.BufferedOutputStream import java.io.BufferedReader @@ -16,6 +18,7 @@ import java.io.InputStreamReader import java.io.OutputStream import java.io.RandomAccessFile import java.io.UnsupportedEncodingException +import java.nio.charset.StandardCharsets import java.util.Enumeration import java.util.zip.ZipEntry import java.util.zip.ZipFile @@ -387,4 +390,31 @@ object FileUtils { output.close() input.close() } + + fun writePackageName(packageName: String) { + val file = File( + Environment.getExternalStorageDirectory(), + "script/packagesname.txt" + ) + val parentDir = file.getParentFile() + if (parentDir != null && !parentDir.exists()) { + val dirsCreated = parentDir.mkdirs() + if (!dirsCreated) { + Log.e("FileWrite", "Failed to create directories: $parentDir") + return + } + } + LogUtils.d("TAG", "writePackageName: $packageName", null) + try { + BufferedOutputStream( + FileOutputStream(file) + ).use { bos -> + bos.write(packageName.toByteArray(StandardCharsets.UTF_8)) + bos.flush() // 确保数据写入磁盘 + } + } catch (e: IOException) { + Log.e("FileWrite", "Failed to write package name: $packageName", e) + // 6. 可以考虑添加重试机制或通知用户 + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/util/ShellUtils.java b/app/src/main/java/com/android/grape/util/ShellUtils.java index 4b886a6..529c423 100644 --- a/app/src/main/java/com/android/grape/util/ShellUtils.java +++ b/app/src/main/java/com/android/grape/util/ShellUtils.java @@ -185,9 +185,9 @@ public class ShellUtils { public static void execRootCmd(String cmd) { // 校验命令是否安全 - if (!isCommandSafe(cmd)) { - return; - } +// if (!isCommandSafe(cmd)) { +// return; +// } List cmds = new ArrayList<>(); cmds.add(cmd); diff --git a/app/src/main/java/com/android/grape/util/Util.kt b/app/src/main/java/com/android/grape/util/Util.kt index 54be1b6..c9f57c5 100644 --- a/app/src/main/java/com/android/grape/util/Util.kt +++ b/app/src/main/java/com/android/grape/util/Util.kt @@ -1140,7 +1140,7 @@ object Util { return false } - unzipAPkSh(script_path, "/sdcard/autojs/") + unzipAPkSh(script_path, "/sdcard/script/") delFileSh(script_path) } return isDownload @@ -2571,9 +2571,9 @@ object Util { */ //当本应用位于后台时,则将它切换到最前端 fun setTopApp(context: Context) { -// Log.i(TAG, "start to setTopApp"); + Log.i(TAG, "start to setTopApp"); if (isRunningForeground(context)) { -// Log.i(TAG, "app isRunningForeground"); + Log.i(TAG, "app isRunningForeground"); return; } //获取ActivityManager