打包APK签名问题
作者您好,我在使用项目中遇到了如下问题,希望解惑一下: 我想获取设备唯一标识,IMEI无法获取,只能使用device.getAndroidId()。 但是在脚本中使用获得的,与打包APK之后获得的,不是一个。 百度搜了下发现是因为两个软件签名不一致,所以安卓系统返回的id也就不一样了,请问如何解决? 目前想法是打包的APK使用的签名与autojs6的APK的签名保持一致,就可以保持androidId不变,但是不知道如何实现,不确定是否需要修改源码。
您好, 感谢反馈.
@SuppressLint("HardwareIds")
public String getAndroidId() {
return Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.ANDROID_ID);
}
不考虑多用户的情况下, 上述代码在同一个设备 (Android 8.0+) 不同签名的应用中, 获取到的值是不一致的.
可以发现, 读取 ANDROID_ID 并不需要额外的权限, 但它不适合作为跨应用跨设备的 "设备唯一标识", 否则它很容易作为重要的隐私信息之一被任意应用获取.
另外, 签名一致手段虽然理论上可行, 但 AutoJs6 的签名私钥是不可公开的 (它相当于身份证这样的身份证明, 公开后任何开发者都能够以我的名义发布 AutoJs6 应用).
我们可以换一个角度, 不依赖 ANDROID_ID. 因为你的使用范围以 "设备" 为单位, 只要保证同一设备中, AutoJs6 与其所有打包应用可以共享同一个唯一标识, 就可以实现目标.
如果优先考虑不打扰用户去选则目录或文件 (即不需要用户交互), 可以把新生成的一个 UUID 作为 "共享设备 ID" 放在 AutoJs6 内, 并通过一个 ContentProvider 对外只读暴露. 思路如下:
- 共享设备 ID 的唯一来源在 AutoJs6 本体应用内: 首次生成
UUID并持久化 (可能会考虑私有文件方式) - 通过一个只读
ContentProvider对外暴露读取接口; 任意应用均可读, 但不可写 - 封装方法挂载到全局模块 (如
device模块), 内部实现主要通过ContentResolver.query静默获取共享设备 ID
未来 AutoJs6 会考虑支持一个 device.getSharedDeviceId() 方法, 使用方法如下:
const sharedDeviceId = device.getSharedDeviceId();
toastlog(sharedDeviceId);
功能实现之后, 所有 AutoJs6 的打包应用与 AutoJs6 本体应用运行上述代码, 都会获取到相同的结果.
内部关键代码:
<manifest ... >
<application ... >
<provider
android:name="org.autojs.autojs.deviceid.DeviceIdProvider"
android:authorities="${applicationId}.deviceid.provider"
android:exported="true" />
</application>
</manifest>
class DeviceIdProvider : ContentProvider() {
companion object {
private const val AUTH = "org.autojs.autojs6.deviceid.provider"
private const val PATH = "v1/device_id"
private const val CODE = 1
private const val COL = "device_id"
private const val SP_NAME = "device_id_prefs"
private const val SP_KEY = "device_id"
private val matcher = UriMatcher(UriMatcher.NO_MATCH).apply {
addURI(AUTH, PATH, CODE)
}
}
override fun onCreate(): Boolean {
ensureId()
return true
}
private fun ensureId(): String {
val sp = context!!.getSharedPreferences(SP_NAME, 0)
val exist = sp.getString(SP_KEY, null)
if (exist != null) return exist
val id = UUID.randomUUID().toString()
sp.edit { putString(SP_KEY, id) }
return id
}
override fun query(
uri: Uri, projection: Array<out String>?, selection: String?,
selectionArgs: Array<out String>?, sortOrder: String?,
): Cursor? {
if (matcher.match(uri) != CODE) return null
val id = ensureId()
val cursor = MatrixCursor(arrayOf(COL), 1)
cursor.addRow(arrayOf(id))
return cursor
}
override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
if (method == "getDeviceId") {
val id = ensureId()
return Bundle().apply { putString(COL, id) }
}
return super.call(method, arg, extras)
}
override fun getType(uri: Uri) = "vnd.android.cursor.item/$AUTH.$PATH"
override fun insert(uri: Uri, values: ContentValues?) = null
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?) = 0
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?) = 0
}
fun getSharedDeviceId(scriptRuntime: ScriptRuntime, args: Array<out Any?>): String? = ensureArgumentsIsEmpty(args) {
val uri = "content://org.autojs.autojs6.deviceid.provider/v1/device_id".toUri()
globalContext.contentResolver.query(uri, arrayOf("device_id"), null, null, null)?.use { c ->
if (c.moveToFirst()) c.getString(0) else null
}
}
注: 示例代码没有使用私有文件方式, 而是 SharedPreferences 方式.
另外, 未来同时会考虑在 AutoJs6 应用设置中, 针对共享设备 ID 提供 [ 修改/重置/关闭共享 ] 等相关设置选项.
如有其他疑问, 欢迎继续反馈.
感谢作者耐心的回答,足够解惑了~ 并感谢给出的解决方案~ 学习学习