check redelivered Intents whether they completed while we were killed

If F-Droid gets killed during the install/update process, then the install
procedure would keep getting readded and redownloaded since it is a sticky
Intent.  The test is very specific so that this does not block things like
installing updates with the same versionCode, which happens sometimes, and
is allowed by Android.

This commit is contained in:
Hans-Christoph Steiner 2018-04-11 11:30:14 +02:00
parent 1413c35342
commit b523ecc969

@ -7,11 +7,12 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.net.Uri;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.fdroid.fdroid.AppUpdateStatusManager;
@ -127,6 +128,19 @@ public class InstallManagerService extends Service {
super.onDestroy();
}
/**
* This goes through a series of checks to make sure that the incoming
* {@link Intent} is still valid. The default {@link Intent#getAction() action}
* in the logic is {@link #ACTION_INSTALL} since it is the most complicate
* case. Since the {@code Intent} will be redelivered by Android if the
* app was killed, this needs to check that it still makes sense to handle.
* <p>
* For example, if F-Droid is killed while installing, it might not receive
* the message that the install completed successfully. The checks need to be
* as specific as possible so as not to block things like installing updates
* with the same {@link PackageInfo#versionCode}, which happens sometimes,
* and is allowed by Android.
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Utils.debugLog(TAG, "onStartCommand " + intent);
@ -155,7 +169,7 @@ public class InstallManagerService extends Service {
appUpdateStatusManager.removeApk(urlString);
return START_NOT_STICKY;
} else if (!ACTION_INSTALL.equals(action)) {
Utils.debugLog(TAG, "Ignoring " + intent + " as it is not an " + ACTION_INSTALL + " intent");
Log.i(TAG, "Ignoring unknown intent action: " + intent);
return START_NOT_STICKY;
}
@ -166,7 +180,6 @@ public class InstallManagerService extends Service {
if ((flags & START_FLAG_REDELIVERY) == START_FLAG_REDELIVERY
&& !DownloaderService.isQueuedOrActive(urlString)) {
// TODO is there a case where we should allow an active urlString to pass through?
Utils.debugLog(TAG, urlString + " finished downloading while InstallManagerService was killed.");
appUpdateStatusManager.removeApk(urlString);
return START_NOT_STICKY;
@ -179,6 +192,14 @@ public class InstallManagerService extends Service {
return START_NOT_STICKY;
}
PackageInfo packageInfo = Utils.getPackageInfo(this, apk.packageName);
if ((flags & START_FLAG_REDELIVERY) == START_FLAG_REDELIVERY
&& packageInfo != null && packageInfo.versionCode == apk.versionCode
&& TextUtils.equals(packageInfo.versionName, apk.versionName)) {
Log.i(TAG, "INSTALL Intent no longer valid since its installed, ignoring: " + intent);
return START_NOT_STICKY;
}
FDroidApp.resetMirrorVars();
DownloaderService.setTimeout(FDroidApp.getTimeout());