diff --git a/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java b/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java index cd74bd0d9..df139905c 100644 --- a/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java +++ b/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java @@ -1,17 +1,41 @@ package org.fdroid.fdroid.views.panic; +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; +import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AppCompatActivity; import android.util.Log; - -import org.fdroid.fdroid.Preferences; -import org.fdroid.fdroid.views.hiding.HidingManager; - import info.guardianproject.panic.Panic; import info.guardianproject.panic.PanicResponder; +import org.fdroid.fdroid.Preferences; +import org.fdroid.fdroid.data.Apk; +import org.fdroid.fdroid.data.InstalledApp; +import org.fdroid.fdroid.data.InstalledAppProvider; +import org.fdroid.fdroid.installer.Installer; +import org.fdroid.fdroid.installer.InstallerService; +import org.fdroid.fdroid.installer.PrivilegedInstaller; +import org.fdroid.fdroid.views.hiding.HidingManager; +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * This {@link Activity} is purely to run events in response to a panic trigger. + * It needs to be an {@code Activity} rather than a {@link android.app.Service} + * so that it can fetch some of the required information about what sent the + * {@link Intent}. This is therefore an {@code Activity} without any UI, which + * is a special case in Android. All the code must be in + * {@link #onCreate(Bundle)} and {@link #finish()} must be called at the end of + * that method. + * + * @see PanicResponder#receivedTriggerFromConnectedApp(Activity) + */ public class PanicResponderActivity extends AppCompatActivity { private static final String TAG = PanicResponderActivity.class.getSimpleName(); @@ -29,12 +53,65 @@ public class PanicResponderActivity extends AppCompatActivity { // received intent from panic app Log.i(TAG, "Received Panic Trigger..."); - Preferences preferences = Preferences.get(); + final Preferences preferences = Preferences.get(); - if (PanicResponder.receivedTriggerFromConnectedApp(this)) { - Log.i(TAG, "Panic Trigger came from connected app"); + boolean receivedTriggerFromConnectedApp = PanicResponder.receivedTriggerFromConnectedApp(this); + final boolean runningAppUninstalls = PrivilegedInstaller.isDefault(this); - // Performing destructive panic responses + ArrayList wipeList = new ArrayList<>(preferences.getPanicWipeSet()); + preferences.setPanicWipeSet(Collections.emptySet()); + preferences.setPanicTmpSelectedSet(Collections.emptySet()); + + if (receivedTriggerFromConnectedApp && runningAppUninstalls && wipeList.size() > 0) { + + // if this app (e.g. F-Droid) is to be deleted, do it last + if (wipeList.contains(getPackageName())) { + wipeList.remove(getPackageName()); + wipeList.add(getPackageName()); + } + + final Context context = this; + final CountDownLatch latch = new CountDownLatch(1); + final LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context); + final String lastToUninstall = wipeList.get(wipeList.size() - 1); + final BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + switch ((intent.getAction())) { + case Installer.ACTION_UNINSTALL_INTERRUPTED: + case Installer.ACTION_UNINSTALL_COMPLETE: + latch.countDown(); + break; + } + } + }; + lbm.registerReceiver(receiver, Installer.getUninstallIntentFilter(lastToUninstall)); + + for (String packageName : wipeList) { + InstalledApp installedApp = InstalledAppProvider.Helper.findByPackageName(context, packageName); + InstallerService.uninstall(context, new Apk(installedApp)); + } + + // wait for apps to uninstall before triggering final responses + new Thread() { + @Override + public void run() { + try { + latch.await(10, TimeUnit.MINUTES); + } catch (InterruptedException e) { + // ignored + } + lbm.unregisterReceiver(receiver); + if (preferences.panicHide()) { + HidingManager.hide(context); + } + if (preferences.panicExit()) { + exitAndClear(); + } + } + }.start(); + } else if (receivedTriggerFromConnectedApp) { + // Performing destructive panic response if (preferences.panicHide()) { Log.i(TAG, "Hiding app..."); HidingManager.hide(this); @@ -42,13 +119,16 @@ public class PanicResponderActivity extends AppCompatActivity { } // exit and clear, if not deactivated - if (preferences.panicExit()) { - ExitActivity.exitAndRemoveFromRecentApps(this); - if (Build.VERSION.SDK_INT >= 21) { - finishAndRemoveTask(); - } + if (!runningAppUninstalls && preferences.panicExit()) { + exitAndClear(); } finish(); } + private void exitAndClear() { + ExitActivity.exitAndRemoveFromRecentApps(this); + if (Build.VERSION.SDK_INT >= 21) { + finishAndRemoveTask(); + } + } }