使用 Device Policy Controller 在后台升级应用程序
Upgrading app in background using Device Policy Controller
我有一个工作的 DPC 应用程序,它是设备所有者。我已经在两个不同的 Android 6.0.1 设备上进行了尝试,以排除任何 device/manufacturer 问题。
我使用 adb shell dpm set-device-owner com.example.dpc/.DpcDeviceAdminReceiver
将我的 DPC 应用设置为所有者。使其成为所有者后,它可以正确地向另一个应用程序授予 COSU 权限,这让我确信这是有效的。命令返回响应:
Success: Device owner set to package com.example.dpc
Active admin set to component {com.examplem.dpc/com.example.dpc.DpcDeviceAdminReceiver}
我想使用此应用安装和升级另一个应用,无需用户干预(就像 Google Play 那样)。
我正在使用以下概念验证代码:
void upgrade() {
String apkFileName = "app_debug_2_0_0";
PackageManager packageManger = getPackageManager();
PackageInstaller packageInstaller = packageManger.getPackageInstaller();
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
params.setAppPackageName("com.example.dummy");
try {
Log.e(TAG, "apkFileName " + apkFileName);
InputStream ins = getResources().openRawResource(
getResources().getIdentifier(apkFileName,
"raw", getPackageName()));
int sessionId = packageInstaller.createSession(params);
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
OutputStream out = session.openWrite(apkFileName, 0, -1);
final int bufsize = 4096;
byte[] bytes = new byte[bufsize];
int len = 1; // any value > 0
int tot = 0;
while (len > 0) {
len = ins.read(bytes, 0, bufsize);
Log.d(TAG, "len: " + len);
if (len < 1) break;
out.write(bytes, 0, len);
tot += len;
Log.d(TAG, "copied: " + tot);
}
ins.close();
session.fsync(out);
out.close();
Log.e(TAG, "about to commit ");
session.commit(PendingIntent.getBroadcast(this, sessionId,
new Intent("com.example.dpc.intent.UPDATE"), 0).getIntentSender());
Log.e(TAG, "committed ");
} catch (IOException e) {
Log.e(TAG, "Error installing package " + apkFileName, e);
}
}
使用上面的代码和变体,我收到一个包含错误的 com.example.dpc.intent.UPDATE
意图:
Intent {
act=com.example.dpc.intent.UPDATE
flg=0x10
cmp=com.example.dpc/.DpcUpdateReceiver
bqHint=4
(has extras)
}
Bundle[
android.content.pm.extra.STATUS=4,
android.content.pm.extra.PACKAGE_NAME=com.example.dummy,
android.content.pm.extra.SESSION_ID=1055214117,
android.content.pm.extra.LEGACY_STATUS=-15,
android.content.pm.extra.STATUS_MESSAGE=INSTALL_FAILED_TEST_ONLY: installPackageLI
]
Logcat 正确报告正在流式传输到 session.openWrite
流中的 apk
的大小。
我已经看过了:
- gradle 版本 - 我正在使用 Android Studio 和 Gradle v3.0.0 而不是不稳定的版本。
- 从 res/raw
apk
文件复制的字节数:1418604 是正确的。
- 清单 - 没有 testOnly 属性。 https://developer.android.com/guide/topics/manifest/application-element.html#testOnly
我做错了什么?
错误来自 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
中的以下 Android 6.0.1 代码。
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
res.setError(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
return;
}
}
我检查了我尝试安装的应用程序,发现 FLAG_TEST_ONLY
位已设置。
我有一个工作的 DPC 应用程序,它是设备所有者。我已经在两个不同的 Android 6.0.1 设备上进行了尝试,以排除任何 device/manufacturer 问题。
我使用 adb shell dpm set-device-owner com.example.dpc/.DpcDeviceAdminReceiver
将我的 DPC 应用设置为所有者。使其成为所有者后,它可以正确地向另一个应用程序授予 COSU 权限,这让我确信这是有效的。命令返回响应:
Success: Device owner set to package com.example.dpc
Active admin set to component {com.examplem.dpc/com.example.dpc.DpcDeviceAdminReceiver}
我想使用此应用安装和升级另一个应用,无需用户干预(就像 Google Play 那样)。
我正在使用以下概念验证代码:
void upgrade() {
String apkFileName = "app_debug_2_0_0";
PackageManager packageManger = getPackageManager();
PackageInstaller packageInstaller = packageManger.getPackageInstaller();
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
params.setAppPackageName("com.example.dummy");
try {
Log.e(TAG, "apkFileName " + apkFileName);
InputStream ins = getResources().openRawResource(
getResources().getIdentifier(apkFileName,
"raw", getPackageName()));
int sessionId = packageInstaller.createSession(params);
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
OutputStream out = session.openWrite(apkFileName, 0, -1);
final int bufsize = 4096;
byte[] bytes = new byte[bufsize];
int len = 1; // any value > 0
int tot = 0;
while (len > 0) {
len = ins.read(bytes, 0, bufsize);
Log.d(TAG, "len: " + len);
if (len < 1) break;
out.write(bytes, 0, len);
tot += len;
Log.d(TAG, "copied: " + tot);
}
ins.close();
session.fsync(out);
out.close();
Log.e(TAG, "about to commit ");
session.commit(PendingIntent.getBroadcast(this, sessionId,
new Intent("com.example.dpc.intent.UPDATE"), 0).getIntentSender());
Log.e(TAG, "committed ");
} catch (IOException e) {
Log.e(TAG, "Error installing package " + apkFileName, e);
}
}
使用上面的代码和变体,我收到一个包含错误的 com.example.dpc.intent.UPDATE
意图:
Intent {
act=com.example.dpc.intent.UPDATE
flg=0x10
cmp=com.example.dpc/.DpcUpdateReceiver
bqHint=4
(has extras)
}
Bundle[
android.content.pm.extra.STATUS=4,
android.content.pm.extra.PACKAGE_NAME=com.example.dummy,
android.content.pm.extra.SESSION_ID=1055214117,
android.content.pm.extra.LEGACY_STATUS=-15,
android.content.pm.extra.STATUS_MESSAGE=INSTALL_FAILED_TEST_ONLY: installPackageLI
]
Logcat 正确报告正在流式传输到 session.openWrite
流中的 apk
的大小。
我已经看过了:
- gradle 版本 - 我正在使用 Android Studio 和 Gradle v3.0.0 而不是不稳定的版本。
- 从 res/raw
apk
文件复制的字节数:1418604 是正确的。 - 清单 - 没有 testOnly 属性。 https://developer.android.com/guide/topics/manifest/application-element.html#testOnly
我做错了什么?
错误来自 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
中的以下 Android 6.0.1 代码。
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
res.setError(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
return;
}
}
我检查了我尝试安装的应用程序,发现 FLAG_TEST_ONLY
位已设置。