From 72f5398b7903e42fe4de841269ac9e1a768979af Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 29 May 2019 17:20:09 +0200 Subject: [PATCH] panic: uninstall apps based destructive panic trigger This will uninstall the list of apps that the user has setup in the Panic Settings if Privileged Extension is installed. This also requires that the user set up a trusted connection between a panic trigger app (e.g. Ripple) and F-Droid. --- .../views/panic/PanicResponderActivity.java | 106 +++++++++++++++--- 1 file changed, 93 insertions(+), 13 deletions(-) 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(); + } + } }