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.