问题描述
我们的应用程序从我们的服务器下载APK,并运行此升级本身.如 Android 10 - 没有发现的活动来处理意图和 xamarin android 10安装apk - 没有找到活动为了处理意图,如果移动设备已被升级到Android 10,则此前不起作用,因此"未找到要处理意图"的"无活动".
我们尝试使用ProdantInstaller作为示例中的重写 https://android.googlesource.com/platform/development/+/master/comples/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 for indent android函数这样的java.
一些想法:
*在 Xamarin Android 10安装APK - 没有活动发现要处理意图,他们提到他们需要在Xamarin做额外的垃圾收集.也许它是因为我们在这里有同样的问题?如果是这样,我们如何直接在Java中传递这个?
*我们正在尝试使用下载的APK升级相同的应用程序.这项工作都会使用包安装程序吗?如果没有,我们是否需要使用第二个应用程序来升级原件?
*我们还有一个在应用程序中运行的服务,也可以导致"文件仍然打开"问题?
推荐答案
我能够通过关闭InputStream和OutputStream来解决此问题.此外,我必须在21之前检查SDK版本,因为我们有最小API 16,并且在API 21中添加了PartinalLInstaller.
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()); } }
问题描述
Our app downloads an APK from our server, and runs this to upgrade itself. As mentioned in Android 10 - No Activity found to handle Intent and Xamarin Android 10 Install APK - No Activity found to handle Intent, this does not work as previously if the mobile device has been upgraded to Android 10, getting "No Activity found to handle Intent".
We've tried to rewrite this using PackageInstaller as in the example https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/content/InstallApkSessionApi.java, but now get this error instead:
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)
Here's the code we use:
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(); } } }
Target SDK is set to API 23 to be able to support old devices some customers have. We're using Qt as the app framework, but Java for native Android functions like this.
Some thoughts on this:
* In Xamarin Android 10 Install APK - No Activity found to handle Intent, they mention that they need to do extra garbage collection in Xamarin. Maybe it's because of the same issue we have here? If so, how could we get passed this in Java directly?
* We're trying to upgrade the same app using a downloaded apk. Will this work at all using package installer? If not, do we then need to use a second app to upgrade the original?
* We also have a service running in the app as well for notifications, could this be causing the "Files still open" issue?
推荐答案
I was able to solve this by closing the InputStream and OutputStream. In addition, I had to check for SDK versions prior to 21, as we have minimum API 16 and PackageInstaller was added in API 21.
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()); } }