在Android 10上安装更新相同应用程序的apk失败; java.lang.SecurityException:文件



我们的应用程序从我们的服务器下载一个APK,并运行它进行自我升级。如 Android 10 - 未找到处理意图的活动和 Xamarin Android 10 安装 APK - 未找到处理意图的活动中所述,如果移动设备已升级到 Android 10,则这不会像以前一样工作,得到"未找到处理意图的活动"。

我们尝试使用 PackageInstaller 重写它,如示例中所示 https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/content/InstallApkSessionApi.java,但现在收到此错误:

signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------
Abort message: 'JNI DETECTED ERROR IN APPLICATION: JNI GetStaticMethodID called with pending exception java.lang.SecurityException: Files still open
at java.lang.Exception android.os.Parcel.createException(int, java.lang.String) (Parcel.java:2071)
at void android.os.Parcel.readException(int, java.lang.String) (Parcel.java:2039)
at void android.os.Parcel.readException() (Parcel.java:1987)
at void android.content.pm.IPackageInstallerSession$Stub$Proxy.commit(android.content.IntentSender, boolean) (IPackageInstallerSession.java:593)
at void android.content.pm.PackageInstaller$Session.commit(android.content.IntentSender) (PackageInstaller.java:1072)
at void com.mycompany.myApp.QtJavaCustomBridge.JIntentActionInstallApk(java.lang.String) (QtJavaCustomBridge.java:301)
at void org.qtproject.qt5.android.QtNative.startQtApplication() (QtNative.java:-2)
at void org.qtproject.qt5.android.QtNative$7.run() (QtNative.java:374)
at void org.qtproject.qt5.android.QtThread$1.run() (QtThread.java:61)
at void java.lang.Thread.run() (Thread.java:919)
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.pm.PackageInstallerSession.assertNoWriteFileTransfersOpenLocked(PackageInstallerSession.java:837)
at com.android.server.pm.PackageInstallerSession.sealAndValidateLocked(PackageInstallerSession.java:1094)
at com.android.server.pm.PackageInstallerSession.markAsCommitted(PackageInstallerSession.java:987)
at com.android.server.pm.PackageInstallerSession.commit(PackageInstallerSession.java:849)
at android.content.pm.IPackageInstallerSession$Stub.onTransact(IPackageInstallerSession.java:306)
(Throwable with no stack trace)

以下是我们使用的代码:

public static final String TAG = "JAVA"; 
public static final String PACKAGE_INSTALLED_ACTION = "com.mycompany.myApp.SESSION_API_PACKAGE_INSTALLED";
public static void JIntentActionInstallApk(final String filename)
{
PackageInstaller.Session session = null;
try {
Log.i(TAG, "JIntentActionInstallApk " + filename);
PackageInstaller packageInstaller = MyAppActivity.getActivityInstance().getPackageManager().getPackageInstaller();
Log.i(TAG, "JIntentActionInstallApk - got packageInstaller");
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
Log.i(TAG, "JIntentActionInstallApk - set SessionParams");
int sessionId = packageInstaller.createSession(params);
session = packageInstaller.openSession(sessionId);
Log.i(TAG, "JIntentActionInstallApk - session opened");
// Create an install status receiver.
Context context = MyAppActivity.getActivityInstance().getApplicationContext();
addApkToInstallSession(context, filename, session);
Log.i(TAG, "JIntentActionInstallApk - apk added to session");
Intent intent = new Intent(context, MyAppActivity.class);
intent.setAction(MyAppActivity.PACKAGE_INSTALLED_ACTION);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
IntentSender statusReceiver = pendingIntent.getIntentSender();
// Commit the session (this will start the installation workflow).
session.commit(statusReceiver);
Log.i(TAG, "JIntentActionInstallApk - commited");
} catch (IOException e) {
throw new RuntimeException("Couldn't install package", e);
} catch (RuntimeException e) {
if (session != null) {
session.abandon();
}
throw e;
}
}
private static void addApkToInstallSession(Context context, String filename, PackageInstaller.Session session)
{
Log.i(TAG, "addApkToInstallSession " + filename);
// It's recommended to pass the file size to openWrite(). Otherwise installation may fail
// if the disk is almost full.
try {
OutputStream packageInSession = session.openWrite("package", 0, -1);
InputStream input;
Uri uri = Uri.parse(filename);
input = context.getContentResolver().openInputStream(uri);
if(input != null) {
Log.i(TAG, "input.available: " + input.available());
byte[] buffer = new byte[16384];
int n;
while ((n = input.read(buffer)) >= 0) {
packageInSession.write(buffer, 0, n);
}
}
else {
Log.i(TAG, "addApkToInstallSession failed");
throw new IOException ("addApkToInstallSession");
}
}
catch (Exception e) {
Log.i(TAG, "addApkToInstallSession failed2 " + e.toString());
}
}
...
@Override
protected void onNewIntent(Intent intent) {
Bundle extras = intent.getExtras();
if (PACKAGE_INSTALLED_ACTION.equals(intent.getAction())) {
int status = extras.getInt(PackageInstaller.EXTRA_STATUS);
String message = extras.getString(PackageInstaller.EXTRA_STATUS_MESSAGE);
switch (status) {
case PackageInstaller.STATUS_PENDING_USER_ACTION:
// This test app isn't privileged, so the user has to confirm the install.
Intent confirmIntent = (Intent) extras.get(Intent.EXTRA_INTENT);
startActivity(confirmIntent);
break;
case PackageInstaller.STATUS_SUCCESS:
Toast.makeText(this, "Install succeeded!", Toast.LENGTH_SHORT).show();
break;
case PackageInstaller.STATUS_FAILURE:
case PackageInstaller.STATUS_FAILURE_ABORTED:
case PackageInstaller.STATUS_FAILURE_BLOCKED:
case PackageInstaller.STATUS_FAILURE_CONFLICT:
case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
case PackageInstaller.STATUS_FAILURE_INVALID:
case PackageInstaller.STATUS_FAILURE_STORAGE:
Toast.makeText(this, "Install failed! " + status + ", " + message,
Toast.LENGTH_SHORT).show();
break;
default:
Toast.makeText(this, "Unrecognized status received from installer: " + status,
Toast.LENGTH_SHORT).show();
}
}
}

目标 SDK 设置为 API 23,以便能够支持某些客户拥有的旧设备。我们使用Qt作为应用程序框架,但Java的原生Android功能像这样。

对此的一些想法: * 在 Xamarin
Android 10 安装 APK - 没有找到处理意图的活动,他们提到他们需要在 Xamarin 中进行额外的垃圾收集。也许是因为我们在这里有同样的问题?如果是这样,我们如何直接在 Java 中传递它?
*我们正在尝试使用下载的apk升级相同的应用程序。使用包安装程序可以工作吗?如果没有,我们是否需要使用第二个应用程序来升级原始应用程序?
*我们还在应用程序中运行了一项用于通知的服务,这是否会导致"文件仍然打开"问题?

我能够通过关闭InputStream和OutputStream来解决这个问题。此外,我必须检查 21 之前的 SDK 版本,因为我们有最低 API 16 并且 API 21 中添加了 PackageInstaller。

public static void JIntentActionInstallApk(final String filename)
{
PackageInstaller.Session session = null;
try {
Log.i(TAG, "JIntentActionInstallApk " + filename);
if(Build.VERSION.SDK_INT < 21) {
//as PackageInstaller was added in API 21, let's use the old way of doing it prior to 21
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
Uri apkUri = Uri.parse(filename);
Context context = MyAppActivity.getQtActivityInstance().getApplicationContext();
ApplicationInfo appInfo = context.getApplicationInfo();
intent.setData(apkUri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
appInfo.packageName);
MyAppActivity.getQtActivityInstance().startActivity(intent);
}
else  {
// API level 21 or higher, we need to use PackageInstaller
PackageInstaller packageInstaller = MyAppActivity.getQtActivityInstance().getPackageManager().getPackageInstaller();
Log.i(TAG, "JIntentActionInstallApk - got packageInstaller");
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
Log.i(TAG, "JIntentActionInstallApk - set SessionParams");
int sessionId = packageInstaller.createSession(params);
session = packageInstaller.openSession(sessionId);
Log.i(TAG, "JIntentActionInstallApk - session opened");
// Create an install status receiver.
Context context = MyAppActivity.getQtActivityInstance().getApplicationContext();
addApkToInstallSession(context, filename, session);
Log.i(TAG, "JIntentActionInstallApk - apk added to session");
Intent intent = new Intent(context, MyAppActivity.class);
intent.setAction(MyAppActivity.PACKAGE_INSTALLED_ACTION);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
IntentSender statusReceiver = pendingIntent.getIntentSender();
// Commit the session (this will start the installation workflow).
session.commit(statusReceiver);
Log.i(TAG, "JIntentActionInstallApk - commited");
}
} catch (IOException e) {
throw new RuntimeException("Couldn't install package", e);
} catch (RuntimeException e) {
if (session != null) {
session.abandon();
}
throw e;
}
}
private static void addApkToInstallSession(Context context, String filename, PackageInstaller.Session session)
{
Log.i(TAG, "addApkToInstallSession " + filename);
// It's recommended to pass the file size to openWrite(). Otherwise installation may fail
// if the disk is almost full.
try {
OutputStream packageInSession = session.openWrite("package", 0, -1);
InputStream input;
Uri uri = Uri.parse(filename);
input = context.getContentResolver().openInputStream(uri);
if(input != null) {
Log.i(TAG, "input.available: " + input.available());
byte[] buffer = new byte[16384];
int n;
while ((n = input.read(buffer)) >= 0) {
packageInSession.write(buffer, 0, n);
}
}
else {
Log.i(TAG, "addApkToInstallSession failed");
throw new IOException ("addApkToInstallSession");
}
packageInSession.close();  //need to close this stream
input.close();             //need to close this stream
}
catch (Exception e) {
Log.i(TAG, "addApkToInstallSession failed2 " + e.toString());
}
}

最新更新