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.

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

View File

@ -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());