1

We have an enterprise app (not play store) that installs via APK that we distribute. In that app is an auto-update procedure that worked fine until SDK 29. I've been trying to update our code to use the new methodologies for downloading and starting a new intent with the downloaded APK, but its been a frustrating whack-a-mole experience.

My current state is that I CAN download the file and can see that it exists, but if I try to start an intent with it, I get the error about the file not being accessible because its an external file. A lot of googling later, I needed to implement file provider, which I've done, but the paths don't match between where the file is downloaded and what I'm passing to the intent.

Basically, I'm really confused about FileProvider and how I'm supposed to use it to get to the downloaded file path and let Android open the APK to install. Prompting the user to install is fine, that's how its always worked for us, and we're not trying to do a "silent" install. I just want it to work.

Pared down relevant code:

private void downloadUpdate(Uri uri) {
final String filename = File.separator + uri.getLastPathSegment();

DownloadManager downloadmanager = (DownloadManager) parent.getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(uri);
request.setDescription("Downloading");
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalFilesDir(getContext(), Environment.DIRECTORY_DOWNLOADS, filename);

try {
    onDownloadComplete = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
            if (downloadId == id) {
                Toast.makeText(parent, "Download Completed", Toast.LENGTH_SHORT).show();
                try {
                    Intent installIntent = new Intent(Intent.ACTION_VIEW);
                    String filepath = getContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) + filename;
                    File update = new File(filepath);

                    Uri updateUri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".fileprovider", update);

                    installIntent.setDataAndType(updateUri, "application/vnd.android.package-archive");
                    installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    startActivity(installIntent);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    };
    parent.registerReceiver(onDownloadComplete,new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
    downloadId = downloadmanager.enqueue(request);

} catch (Exception ex) {
    Log.e("UpdateDownload", "Update Error: " + ex.getMessage());
    new ErrorLog(new Date(), "Error downloading update: " + ex.getMessage());
}
}

My provider definition in the manifest:

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/filepaths" />
</provider>

And my filepaths xml:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-files-path name="pictures" path="Pictures" />
    <external-files-path name="Download" path="Download/" />
</paths>

Everything downloads fine, and I get all the way to the startActivity line, but then I get an error where android can't parse the package - because its not actually looking at the right file path. In debug mode if I do update.exists(), I get a true, so the file IS being downloaded, but the update filepath does not equal what is found for updateUri.

How do I get fileprovider to provide the proper filepath to the intent? I assume my XML is configured wrong, but I don't understand what exactly the FileProvider is doing here.

Dave Sanders
  • 3,135
  • 4
  • 33
  • 43
  • You cannot use ACTION_VIEW anymore to install an apk file. Solutions have been posted before. – blackapps Aug 04 '21 at 19:33
  • Besides that, if you could use ACTION_VIEW, then using a FileProvider is not needed as DownloadManager will give you an uri for the downloaded file. So use that for old Android versions. – blackapps Aug 04 '21 at 19:36
  • Stop using `DownloadManager`. Download the update yourself (OkHttp, etc.), to some file that you control (e.g., in `getCacheDir()`). Then use `FileProvider` to make that available to the installer. `DownloadManager`, at this point, is fairly useless for downloads where the app requesting the download needs access to the downloaded content. `DownloadManager` now mostly is for cases where the user has requested a download, but the app does not need the download itself (e.g., downloading a PDF from a Web browser). – CommonsWare Aug 04 '21 at 19:41
  • Thanks for the comments from both. Can either of you point me to a good example? I've been googling for days now and between the half solutions posted, the spam, and the changes between Android < 29 and today, I'm having a really hard time finding a good source. – Dave Sanders Aug 05 '21 at 12:49
  • Ok, finding out ACTION_VIEW was deprecated helped a lot @blackapps. That gave me a search term which led me to this, which I will try to implement today. https://stackoverflow.com/questions/58085899/android-10-no-activity-found-to-handle-intent – Dave Sanders Aug 05 '21 at 12:53

0 Answers0