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