purge deprecated, broken ExtensionInstaller, only works < android-20
This code is unmaintained, semi-broken, and only works on old Android versions. Time to go! closes #982
This commit is contained in:
parent
b75fc8e011
commit
ab767ce621
@ -181,11 +181,6 @@
|
|||||||
android:key="privilegedInstaller"
|
android:key="privilegedInstaller"
|
||||||
android:persistent="false"
|
android:persistent="false"
|
||||||
android:dependency="expert"/>
|
android:dependency="expert"/>
|
||||||
<Preference
|
|
||||||
android:title="@string/uninstall_system"
|
|
||||||
android:summary="@string/uninstall_system_summary"
|
|
||||||
android:key="uninstallPrivilegedApp"
|
|
||||||
android:dependency="expert"/>
|
|
||||||
</android.support.v7.preference.PreferenceCategory>
|
</android.support.v7.preference.PreferenceCategory>
|
||||||
|
|
||||||
</android.support.v7.preference.PreferenceScreen>
|
</android.support.v7.preference.PreferenceScreen>
|
||||||
|
@ -43,11 +43,6 @@
|
|||||||
<uses-permission android:name="android.permission.NFC"/>
|
<uses-permission android:name="android.permission.NFC"/>
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
|
|
||||||
<!-- Indicate that F-Droid may request root access (introduced by Koush's Superuser app)
|
|
||||||
This permission is deprecated, but necessary for some old superuser
|
|
||||||
apps to actually grant superuser access to F-Droid. -->
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
|
|
||||||
|
|
||||||
<application>
|
<application>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
@ -191,17 +191,8 @@
|
|||||||
android:finishOnTaskLaunch="true"/>
|
android:finishOnTaskLaunch="true"/>
|
||||||
<activity android:name=".views.ScreenShotsActivity"/>
|
<activity android:name=".views.ScreenShotsActivity"/>
|
||||||
<!-- Note: AppThemeTransparent, this activity shows dialogs only -->
|
<!-- Note: AppThemeTransparent, this activity shows dialogs only -->
|
||||||
<activity
|
|
||||||
android:name=".privileged.install.InstallExtensionDialogActivity"
|
|
||||||
android:theme="@style/AppThemeTransparent"/>
|
|
||||||
<activity android:name=".data.ObbUrlActivity"
|
<activity android:name=".data.ObbUrlActivity"
|
||||||
android:theme="@android:style/Theme.NoDisplay"/>
|
android:theme="@android:style/Theme.NoDisplay"/>
|
||||||
<receiver
|
|
||||||
android:name=".privileged.install.InstallExtensionBootReceiver">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
<!-- Note: AppThemeTransparent, this activity shows dialogs only -->
|
<!-- Note: AppThemeTransparent, this activity shows dialogs only -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".installer.DefaultInstallerActivity"
|
android:name=".installer.DefaultInstallerActivity"
|
||||||
|
@ -91,7 +91,6 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
|
|||||||
public static final String PREF_EXPERT = "expert";
|
public static final String PREF_EXPERT = "expert";
|
||||||
public static final String PREF_FORCE_OLD_INDEX = "forceOldIndex";
|
public static final String PREF_FORCE_OLD_INDEX = "forceOldIndex";
|
||||||
public static final String PREF_PRIVILEGED_INSTALLER = "privilegedInstaller";
|
public static final String PREF_PRIVILEGED_INSTALLER = "privilegedInstaller";
|
||||||
public static final String PREF_UNINSTALL_PRIVILEGED_APP = "uninstallPrivilegedApp";
|
|
||||||
public static final String PREF_LOCAL_REPO_NAME = "localRepoName";
|
public static final String PREF_LOCAL_REPO_NAME = "localRepoName";
|
||||||
public static final String PREF_LOCAL_REPO_HTTPS = "localRepoHttps";
|
public static final String PREF_LOCAL_REPO_HTTPS = "localRepoHttps";
|
||||||
public static final String PREF_LANGUAGE = "language";
|
public static final String PREF_LANGUAGE = "language";
|
||||||
|
@ -1,100 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Blue Jay Wireless
|
|
||||||
* Copyright (C) 2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 3
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.fdroid.fdroid.installer;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import org.fdroid.fdroid.BuildConfig;
|
|
||||||
import org.fdroid.fdroid.data.Apk;
|
|
||||||
import org.fdroid.fdroid.privileged.install.InstallExtensionDialogActivity;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Special Installer that is only useful to install the Privileged Extension apk
|
|
||||||
* as a privileged app into the system partition of Android. It is deprecated
|
|
||||||
* because it cannot work on Android versions newer than {@code android-20} or so,
|
|
||||||
* due to increased SELinux enforcement that restricts what even root can do.
|
|
||||||
* <p/>
|
|
||||||
* This is installer requires user interaction and thus install/uninstall directly
|
|
||||||
* return PendingIntents.
|
|
||||||
*
|
|
||||||
* @see <a href="https://www.androidauthority.com/chainfire-rooting-android-lollipop-541458/">Chainfire talks Android Lollipop and the future of rooting</a>
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class ExtensionInstaller extends Installer {
|
|
||||||
|
|
||||||
ExtensionInstaller(Context context, @NonNull Apk apk) {
|
|
||||||
super(context, apk);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void installPackageInternal(Uri localApkUri, Uri downloadUri) {
|
|
||||||
// extension must be signed with the same public key as main F-Droid
|
|
||||||
// NOTE: Disabled for debug builds to be able to test official extension from repo
|
|
||||||
ApkSignatureVerifier signatureVerifier = new ApkSignatureVerifier(context);
|
|
||||||
if (!BuildConfig.DEBUG &&
|
|
||||||
!signatureVerifier.hasFDroidSignature(new File(localApkUri.getPath()))) {
|
|
||||||
sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED,
|
|
||||||
"APK signature of extension not correct!");
|
|
||||||
}
|
|
||||||
Intent installIntent = new Intent(context, InstallExtensionDialogActivity.class);
|
|
||||||
installIntent.setAction(InstallExtensionDialogActivity.ACTION_INSTALL);
|
|
||||||
installIntent.setData(localApkUri);
|
|
||||||
|
|
||||||
PendingIntent installPendingIntent = PendingIntent.getActivity(
|
|
||||||
context.getApplicationContext(),
|
|
||||||
localApkUri.hashCode(),
|
|
||||||
installIntent,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
|
|
||||||
sendBroadcastInstall(downloadUri,
|
|
||||||
Installer.ACTION_INSTALL_USER_INTERACTION, installPendingIntent);
|
|
||||||
|
|
||||||
// don't use broadcasts for the rest of this special installer
|
|
||||||
sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_COMPLETE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void uninstallPackage() {
|
|
||||||
Intent uninstallIntent = new Intent(context, InstallExtensionDialogActivity.class);
|
|
||||||
uninstallIntent.setAction(InstallExtensionDialogActivity.ACTION_UNINSTALL);
|
|
||||||
|
|
||||||
PendingIntent uninstallPendingIntent = PendingIntent.getActivity(
|
|
||||||
context.getApplicationContext(),
|
|
||||||
apk.packageName.hashCode(),
|
|
||||||
uninstallIntent,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
|
|
||||||
sendBroadcastUninstall(Installer.ACTION_UNINSTALL_USER_INTERACTION, uninstallPendingIntent);
|
|
||||||
|
|
||||||
// don't use broadcasts for the rest of this special installer
|
|
||||||
sendBroadcastUninstall(Installer.ACTION_UNINSTALL_COMPLETE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isUnattended() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -51,9 +51,6 @@ public class InstallerFactory {
|
|||||||
} else if (PrivilegedInstaller.isDefault(context)) {
|
} else if (PrivilegedInstaller.isDefault(context)) {
|
||||||
Utils.debugLog(TAG, "privileged extension correctly installed -> PrivilegedInstaller");
|
Utils.debugLog(TAG, "privileged extension correctly installed -> PrivilegedInstaller");
|
||||||
installer = new PrivilegedInstaller(context, apk);
|
installer = new PrivilegedInstaller(context, apk);
|
||||||
} else if (apk.packageName.equals(PrivilegedInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME)) {
|
|
||||||
// special case for installing "Privileged Extension" with root
|
|
||||||
installer = new ExtensionInstaller(context, apk);
|
|
||||||
} else {
|
} else {
|
||||||
installer = new DefaultInstaller(context, apk);
|
installer = new DefaultInstaller(context, apk);
|
||||||
}
|
}
|
||||||
|
@ -1,248 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 3
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.fdroid.fdroid.privileged.install;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
|
||||||
import org.fdroid.fdroid.Preferences;
|
|
||||||
import org.fdroid.fdroid.R;
|
|
||||||
import org.fdroid.fdroid.installer.PrivilegedInstaller;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import eu.chainfire.libsuperuser.Shell;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Partly based on
|
|
||||||
* http://omerjerk.in/2014/08/how-to-install-an-app-to-system-partition/
|
|
||||||
* https://github.com/omerjerk/RemoteDroid/blob/master/app/src/main/java/in/omerjerk/remotedroid/app/MainActivity.java
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("LineLength")
|
|
||||||
abstract class InstallExtension {
|
|
||||||
|
|
||||||
final Context context;
|
|
||||||
|
|
||||||
private static final String BASE_NAME = "FDroidPrivilegedExtension";
|
|
||||||
private static final String APK_FILE_NAME = BASE_NAME + ".apk";
|
|
||||||
|
|
||||||
InstallExtension(final Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static InstallExtension create(final Context context) {
|
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
|
||||||
return new LollipopImpl(context);
|
|
||||||
}
|
|
||||||
if (Build.VERSION.SDK_INT >= 19) {
|
|
||||||
return new KitKatToLollipopImpl(context);
|
|
||||||
}
|
|
||||||
return new PreKitKatImpl(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
final void runInstall(String apkPath) {
|
|
||||||
onPreInstall();
|
|
||||||
Shell.SU.run(getInstallCommands(apkPath));
|
|
||||||
}
|
|
||||||
|
|
||||||
final void runUninstall() {
|
|
||||||
Shell.SU.run(getUninstallCommands());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract String getSystemFolder();
|
|
||||||
|
|
||||||
void onPreInstall() {
|
|
||||||
// To be overridden by relevant base class[es]
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getWarningString() {
|
|
||||||
return context.getString(R.string.system_install_warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getInstallingString() {
|
|
||||||
return context.getString(R.string.installing);
|
|
||||||
}
|
|
||||||
|
|
||||||
String getInstallPath() {
|
|
||||||
return getSystemFolder() + APK_FILE_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getInstallCommands(String apkPath) {
|
|
||||||
final List<String> commands = new ArrayList<>();
|
|
||||||
commands.add("mount -o rw,remount " + FDroidApp.SYSTEM_DIR_NAME); // remount as read-write
|
|
||||||
commands.addAll(getCopyToSystemCommands(apkPath));
|
|
||||||
commands.add("mv " + getInstallPath() + ".tmp " + getInstallPath());
|
|
||||||
commands.add("sleep 5"); // wait until the app is really installed
|
|
||||||
commands.add("mount -o ro,remount " + FDroidApp.SYSTEM_DIR_NAME); // remount as read-only
|
|
||||||
commands.add("am force-stop " + PrivilegedInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME);
|
|
||||||
commands.addAll(getPostInstallCommands());
|
|
||||||
return commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> getCopyToSystemCommands(String apkPath) {
|
|
||||||
final List<String> commands = new ArrayList<>(2);
|
|
||||||
commands.add("cat " + apkPath + " > " + getInstallPath() + ".tmp");
|
|
||||||
commands.add("chmod 644 " + getInstallPath() + ".tmp");
|
|
||||||
return commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> getPostInstallCommands() {
|
|
||||||
final List<String> commands = new ArrayList<>(1);
|
|
||||||
commands.add("am start -n org.fdroid.fdroid/.privileged.install.InstallExtensionDialogActivity --ez "
|
|
||||||
+ InstallExtensionDialogActivity.ACTION_POST_INSTALL + " true");
|
|
||||||
return commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getUninstallCommands() {
|
|
||||||
final List<String> commands = new ArrayList<>();
|
|
||||||
commands.add("am force-stop " + PrivilegedInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME);
|
|
||||||
commands.add("pm clear " + PrivilegedInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME);
|
|
||||||
commands.add("mount -o rw,remount " + FDroidApp.SYSTEM_DIR_NAME);
|
|
||||||
commands.addAll(getCleanUninstallCommands());
|
|
||||||
commands.add("sleep 5");
|
|
||||||
commands.add("mount -o ro,remount " + FDroidApp.SYSTEM_DIR_NAME);
|
|
||||||
commands.addAll(getPostUninstallCommands());
|
|
||||||
return commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> getCleanUninstallCommands() {
|
|
||||||
final List<String> commands = new ArrayList<>(1);
|
|
||||||
commands.add("rm -f " + getInstallPath());
|
|
||||||
return commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> getPostUninstallCommands() {
|
|
||||||
return new ArrayList<>(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PreKitKatImpl extends InstallExtension {
|
|
||||||
|
|
||||||
PreKitKatImpl(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getSystemFolder() {
|
|
||||||
return FDroidApp.SYSTEM_DIR_NAME + "/app/";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class KitKatToLollipopImpl extends InstallExtension {
|
|
||||||
|
|
||||||
KitKatToLollipopImpl(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On KitKat, "Some system apps are more system than others"
|
|
||||||
* https://github.com/android/platform_frameworks_base/commit/ccbf84f44c9e6a5ed3c08673614826bb237afc54
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected String getSystemFolder() {
|
|
||||||
return FDroidApp.SYSTEM_DIR_NAME + "/priv-app/";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* History of PackageManagerService in Lollipop:
|
|
||||||
* https://github.com/android/platform_frameworks_base/commits/lollipop-release/services/core/java/com/android/server/pm/PackageManagerService.java
|
|
||||||
*/
|
|
||||||
private static class LollipopImpl extends InstallExtension {
|
|
||||||
|
|
||||||
LollipopImpl(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreInstall() {
|
|
||||||
// Setup preference to execute postInstall after reboot
|
|
||||||
Preferences.get().setPostPrivilegedInstall(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getWarningString() {
|
|
||||||
return context.getString(R.string.system_install_warning_lollipop);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getInstallingString() {
|
|
||||||
return context.getString(R.string.system_install_installing_rebooting);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cluster-style layout where each app is placed in a unique directory
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected String getSystemFolder() {
|
|
||||||
return FDroidApp.SYSTEM_DIR_NAME + "/priv-app/" + BASE_NAME + "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create app directory
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected List<String> getCopyToSystemCommands(String apkPath) {
|
|
||||||
List<String> commands = new ArrayList<>(4);
|
|
||||||
commands.add("mkdir -p " + getSystemFolder()); // create app directory if not existing
|
|
||||||
commands.add("chmod 755 " + getSystemFolder());
|
|
||||||
commands.add("cat " + apkPath + " > " + getInstallPath() + ".tmp");
|
|
||||||
commands.add("chmod 644 " + getInstallPath() + ".tmp");
|
|
||||||
return commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NOTE: Only works with reboot
|
|
||||||
*
|
|
||||||
* File observers on /system/priv-app/ have been removed because they don't work with the new
|
|
||||||
* cluser-style layout. See
|
|
||||||
* https://github.com/android/platform_frameworks_base/commit/84e71d1d61c53cd947becc7879e05947be681103
|
|
||||||
*
|
|
||||||
* Related stack overflow post: http://stackoverflow.com/q/26487750
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected List<String> getPostInstallCommands() {
|
|
||||||
List<String> commands = new ArrayList<>(3);
|
|
||||||
commands.add("am broadcast -a android.intent.action.ACTION_SHUTDOWN");
|
|
||||||
commands.add("sleep 1");
|
|
||||||
commands.add("reboot");
|
|
||||||
return commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<String> getCleanUninstallCommands() {
|
|
||||||
final List<String> commands = new ArrayList<>(1);
|
|
||||||
commands.add("rm -rf " + getSystemFolder());
|
|
||||||
return commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<String> getPostUninstallCommands() {
|
|
||||||
List<String> commands = new ArrayList<>(3);
|
|
||||||
commands.add("am broadcast -a android.intent.action.ACTION_SHUTDOWN");
|
|
||||||
commands.add("sleep 1");
|
|
||||||
commands.add("reboot");
|
|
||||||
return commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 3
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.fdroid.fdroid.privileged.install;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import org.fdroid.fdroid.Preferences;
|
|
||||||
|
|
||||||
public class InstallExtensionBootReceiver extends BroadcastReceiver {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED) && Preferences.get().isPostPrivilegedInstall()) {
|
|
||||||
Preferences.get().setPostPrivilegedInstall(false);
|
|
||||||
|
|
||||||
Intent postInstall = new Intent(context.getApplicationContext(), InstallExtensionDialogActivity.class);
|
|
||||||
postInstall.setAction(InstallExtensionDialogActivity.ACTION_POST_INSTALL);
|
|
||||||
postInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
context.startActivity(postInstall);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,336 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 3
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.fdroid.fdroid.privileged.install;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.app.FragmentActivity;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.text.Html;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.ContextThemeWrapper;
|
|
||||||
|
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
|
||||||
import org.fdroid.fdroid.R;
|
|
||||||
import org.fdroid.fdroid.installer.PrivilegedInstaller;
|
|
||||||
import org.fdroid.fdroid.views.main.MainActivity;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import eu.chainfire.libsuperuser.Shell;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: This activity has no view on its own, it displays consecutive dialogs.
|
|
||||||
*/
|
|
||||||
public class InstallExtensionDialogActivity extends FragmentActivity {
|
|
||||||
|
|
||||||
private static final String TAG = "InstallIntoSystem";
|
|
||||||
|
|
||||||
public static final String ACTION_INSTALL = "install";
|
|
||||||
|
|
||||||
public static final String ACTION_UNINSTALL = "uninstall";
|
|
||||||
public static final String ACTION_POST_INSTALL = "post_install";
|
|
||||||
|
|
||||||
private String apkPath;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
// this activity itself has no content view (see manifest)
|
|
||||||
|
|
||||||
if (getIntent().getAction() == null) {
|
|
||||||
Log.e(TAG, "Please define an action!");
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Uri dataUri = getIntent().getData();
|
|
||||||
if (dataUri != null) {
|
|
||||||
File apkFile = new File(dataUri.getPath());
|
|
||||||
apkPath = apkFile.getAbsolutePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (getIntent().getAction()) {
|
|
||||||
case ACTION_UNINSTALL:
|
|
||||||
uninstall();
|
|
||||||
break;
|
|
||||||
case ACTION_INSTALL:
|
|
||||||
askBeforeInstall();
|
|
||||||
break;
|
|
||||||
case ACTION_POST_INSTALL:
|
|
||||||
postInstall();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void askBeforeInstall() {
|
|
||||||
// hack to get theme applied (which is not automatically applied due to activity's Theme.NoDisplay
|
|
||||||
ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
|
|
||||||
|
|
||||||
// not support on Android >= 5.1
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= 22) {
|
|
||||||
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(theme);
|
|
||||||
alertBuilder.setMessage(R.string.system_install_not_supported);
|
|
||||||
alertBuilder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
InstallExtensionDialogActivity.this.setResult(Activity.RESULT_CANCELED);
|
|
||||||
InstallExtensionDialogActivity.this.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
alertBuilder.create().show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(theme);
|
|
||||||
alertBuilder.setTitle(R.string.system_install_question);
|
|
||||||
String message = InstallExtension.create(getApplicationContext()).getWarningString();
|
|
||||||
alertBuilder.setMessage(Html.fromHtml(message));
|
|
||||||
alertBuilder.setPositiveButton(R.string.system_install_button_install, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
checkRootTask.execute();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
alertBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
InstallExtensionDialogActivity.this.setResult(Activity.RESULT_CANCELED);
|
|
||||||
InstallExtensionDialogActivity.this.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
alertBuilder.create().show();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Check for root access
|
|
||||||
*/
|
|
||||||
private final AsyncTask<Void, Void, Boolean> checkRootTask = new AsyncTask<Void, Void, Boolean>() {
|
|
||||||
ProgressDialog progressDialog;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
super.onPreExecute();
|
|
||||||
|
|
||||||
// hack to get theme applied (which is not automatically applied due to activity's Theme.NoDisplay
|
|
||||||
ContextThemeWrapper theme = new ContextThemeWrapper(InstallExtensionDialogActivity.this,
|
|
||||||
FDroidApp.getCurThemeResId());
|
|
||||||
|
|
||||||
progressDialog = new ProgressDialog(theme);
|
|
||||||
progressDialog.setMessage(getString(R.string.requesting_root_access_body));
|
|
||||||
progressDialog.setIndeterminate(true);
|
|
||||||
progressDialog.setCancelable(false);
|
|
||||||
progressDialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Boolean doInBackground(Void... params) {
|
|
||||||
return Shell.SU.available();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Boolean rootGranted) {
|
|
||||||
super.onPostExecute(rootGranted);
|
|
||||||
|
|
||||||
progressDialog.dismiss();
|
|
||||||
|
|
||||||
if (rootGranted) {
|
|
||||||
// root access granted
|
|
||||||
|
|
||||||
switch (getIntent().getAction()) {
|
|
||||||
case ACTION_UNINSTALL:
|
|
||||||
uninstallTask.execute();
|
|
||||||
break;
|
|
||||||
case ACTION_INSTALL:
|
|
||||||
installTask.execute();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// root access denied
|
|
||||||
// hack to get theme applied (which is not automatically applied due to activity's Theme.NoDisplay
|
|
||||||
ContextThemeWrapper theme = new ContextThemeWrapper(InstallExtensionDialogActivity.this,
|
|
||||||
FDroidApp.getCurThemeResId());
|
|
||||||
|
|
||||||
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(theme)
|
|
||||||
.setTitle(R.string.root_access_denied_title)
|
|
||||||
.setMessage(getString(R.string.root_access_denied_body))
|
|
||||||
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
InstallExtensionDialogActivity.this.setResult(Activity.RESULT_CANCELED);
|
|
||||||
InstallExtensionDialogActivity.this.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
alertBuilder.create().show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 2. Install into system
|
|
||||||
*/
|
|
||||||
private final AsyncTask<Void, Void, Void> installTask = new AsyncTask<Void, Void, Void>() {
|
|
||||||
ProgressDialog progressDialog;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
super.onPreExecute();
|
|
||||||
|
|
||||||
// hack to get theme applied (which is not automatically applied due to activity's Theme.NoDisplay
|
|
||||||
ContextThemeWrapper theme = new ContextThemeWrapper(InstallExtensionDialogActivity.this,
|
|
||||||
FDroidApp.getCurThemeResId());
|
|
||||||
|
|
||||||
progressDialog = new ProgressDialog(theme);
|
|
||||||
progressDialog.setMessage(InstallExtension.create(getApplicationContext()).getInstallingString());
|
|
||||||
progressDialog.setIndeterminate(true);
|
|
||||||
progressDialog.setCancelable(false);
|
|
||||||
progressDialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... voids) {
|
|
||||||
InstallExtension.create(getApplicationContext()).runInstall(apkPath);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 3. Verify that install worked
|
|
||||||
*/
|
|
||||||
private void postInstall() {
|
|
||||||
int isInstalledCorrectly =
|
|
||||||
PrivilegedInstaller.isExtensionInstalledCorrectly(this);
|
|
||||||
|
|
||||||
String title;
|
|
||||||
String message;
|
|
||||||
final int result;
|
|
||||||
switch (isInstalledCorrectly) {
|
|
||||||
case PrivilegedInstaller.IS_EXTENSION_INSTALLED_YES:
|
|
||||||
title = getString(R.string.system_install_post_success);
|
|
||||||
message = getString(R.string.system_install_post_success_message);
|
|
||||||
result = Activity.RESULT_OK;
|
|
||||||
break;
|
|
||||||
case PrivilegedInstaller.IS_EXTENSION_INSTALLED_NO:
|
|
||||||
title = getString(R.string.system_install_post_fail);
|
|
||||||
message = getString(R.string.system_install_post_fail_message);
|
|
||||||
result = Activity.RESULT_CANCELED;
|
|
||||||
break;
|
|
||||||
case PrivilegedInstaller.IS_EXTENSION_INSTALLED_SIGNATURE_PROBLEM:
|
|
||||||
title = getString(R.string.system_install_post_fail);
|
|
||||||
message = getString(R.string.system_install_post_fail_message) +
|
|
||||||
"\n\n" + getString(R.string.system_install_denied_signature);
|
|
||||||
result = Activity.RESULT_CANCELED;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("unhandled return");
|
|
||||||
}
|
|
||||||
|
|
||||||
// hack to get theme applied (which is not automatically applied due to activity's Theme.NoDisplay
|
|
||||||
ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
|
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(theme)
|
|
||||||
.setTitle(title)
|
|
||||||
.setMessage(message)
|
|
||||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
InstallExtensionDialogActivity.this.setResult(result);
|
|
||||||
InstallExtensionDialogActivity.this.finish();
|
|
||||||
startActivity(new Intent(InstallExtensionDialogActivity.this, MainActivity.class));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setCancelable(false);
|
|
||||||
builder.create().show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void uninstall() {
|
|
||||||
// hack to get theme applied (which is not automatically applied due to activity's Theme.NoDisplay
|
|
||||||
ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
|
|
||||||
|
|
||||||
final boolean isInstalled = PrivilegedInstaller.isExtensionInstalled(this);
|
|
||||||
|
|
||||||
if (isInstalled) {
|
|
||||||
String message = InstallExtension.create(getApplicationContext()).getWarningString();
|
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(theme)
|
|
||||||
.setTitle(R.string.system_uninstall)
|
|
||||||
.setMessage(Html.fromHtml(message))
|
|
||||||
.setPositiveButton(R.string.system_uninstall_button, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
checkRootTask.execute();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
InstallExtensionDialogActivity.this.setResult(Activity.RESULT_CANCELED);
|
|
||||||
InstallExtensionDialogActivity.this.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.create().show();
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("Uninstall invoked, but extension is not installed!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final AsyncTask<Void, Void, Void> uninstallTask = new AsyncTask<Void, Void, Void>() {
|
|
||||||
ProgressDialog progressDialog;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
super.onPreExecute();
|
|
||||||
|
|
||||||
// hack to get theme applied (which is not automatically applied due to activity's Theme.NoDisplay
|
|
||||||
ContextThemeWrapper theme = new ContextThemeWrapper(InstallExtensionDialogActivity.this,
|
|
||||||
FDroidApp.getCurThemeResId());
|
|
||||||
|
|
||||||
progressDialog = new ProgressDialog(theme);
|
|
||||||
progressDialog.setMessage(getString(R.string.uninstalling));
|
|
||||||
progressDialog.setIndeterminate(true);
|
|
||||||
progressDialog.setCancelable(false);
|
|
||||||
progressDialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... voids) {
|
|
||||||
InstallExtension.create(getApplicationContext()).runUninstall();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Void unused) {
|
|
||||||
super.onPostExecute(unused);
|
|
||||||
|
|
||||||
progressDialog.dismiss();
|
|
||||||
|
|
||||||
// app is uninstalled but still display, kill it!
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -44,7 +44,6 @@ import android.text.TextUtils;
|
|||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import info.guardianproject.netcipher.NetCipher;
|
import info.guardianproject.netcipher.NetCipher;
|
||||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||||
import org.fdroid.fdroid.AppDetails2;
|
|
||||||
import org.fdroid.fdroid.CleanCacheService;
|
import org.fdroid.fdroid.CleanCacheService;
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
import org.fdroid.fdroid.Languages;
|
import org.fdroid.fdroid.Languages;
|
||||||
@ -100,7 +99,6 @@ public class PreferencesFragment extends PreferenceFragment
|
|||||||
private SwitchPreference enableProxyCheckPref;
|
private SwitchPreference enableProxyCheckPref;
|
||||||
private SwitchPreference useTorCheckPref;
|
private SwitchPreference useTorCheckPref;
|
||||||
private Preference updateAutoDownloadPref;
|
private Preference updateAutoDownloadPref;
|
||||||
private Preference updatePrivilegedExtensionPref;
|
|
||||||
private CheckBoxPreference keepInstallHistoryPref;
|
private CheckBoxPreference keepInstallHistoryPref;
|
||||||
private Preference installHistoryPref;
|
private Preference installHistoryPref;
|
||||||
private long currentKeepCacheTime;
|
private long currentKeepCacheTime;
|
||||||
@ -123,7 +121,6 @@ public class PreferencesFragment extends PreferenceFragment
|
|||||||
useTorCheckPref = (SwitchPreference) findPreference(Preferences.PREF_USE_TOR);
|
useTorCheckPref = (SwitchPreference) findPreference(Preferences.PREF_USE_TOR);
|
||||||
enableProxyCheckPref = (SwitchPreference) findPreference(Preferences.PREF_ENABLE_PROXY);
|
enableProxyCheckPref = (SwitchPreference) findPreference(Preferences.PREF_ENABLE_PROXY);
|
||||||
updateAutoDownloadPref = findPreference(Preferences.PREF_AUTO_DOWNLOAD_INSTALL_UPDATES);
|
updateAutoDownloadPref = findPreference(Preferences.PREF_AUTO_DOWNLOAD_INSTALL_UPDATES);
|
||||||
updatePrivilegedExtensionPref = findPreference(Preferences.PREF_UNINSTALL_PRIVILEGED_APP);
|
|
||||||
|
|
||||||
overWifiSeekBar = (LiveSeekBarPreference) findPreference(Preferences.PREF_OVER_WIFI);
|
overWifiSeekBar = (LiveSeekBarPreference) findPreference(Preferences.PREF_OVER_WIFI);
|
||||||
overWifiPrevious = overWifiSeekBar.getValue();
|
overWifiPrevious = overWifiSeekBar.getValue();
|
||||||
@ -402,28 +399,6 @@ public class PreferencesFragment extends PreferenceFragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initUpdatePrivilegedExtensionPreference() {
|
|
||||||
if (Build.VERSION.SDK_INT > 19) {
|
|
||||||
// this will never work on newer Android versions, so hide it
|
|
||||||
otherPrefGroup.removePreference(updatePrivilegedExtensionPref);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
updatePrivilegedExtensionPref.setPersistent(false);
|
|
||||||
updatePrivilegedExtensionPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
|
||||||
// Open details of F-Droid Privileged
|
|
||||||
Intent intent = new Intent(getActivity(), AppDetails2.class);
|
|
||||||
intent.putExtra(AppDetails2.EXTRA_APPID,
|
|
||||||
PrivilegedInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME);
|
|
||||||
startActivity(intent);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a user specifies they want to fetch updates automatically, then start the download of relevant
|
* If a user specifies they want to fetch updates automatically, then start the download of relevant
|
||||||
* updates as soon as they enable the feature.
|
* updates as soon as they enable the feature.
|
||||||
@ -490,7 +465,6 @@ public class PreferencesFragment extends PreferenceFragment
|
|||||||
|
|
||||||
initAutoFetchUpdatesPreference();
|
initAutoFetchUpdatesPreference();
|
||||||
initPrivilegedInstallerPreference();
|
initPrivilegedInstallerPreference();
|
||||||
initUpdatePrivilegedExtensionPreference();
|
|
||||||
initUseTorPreference();
|
initUseTorPreference();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,11 +207,6 @@
|
|||||||
android:key="privilegedInstaller"
|
android:key="privilegedInstaller"
|
||||||
android:persistent="false"
|
android:persistent="false"
|
||||||
android:dependency="expert"/>
|
android:dependency="expert"/>
|
||||||
<Preference
|
|
||||||
android:title="@string/uninstall_system"
|
|
||||||
android:summary="@string/uninstall_system_summary"
|
|
||||||
android:key="uninstallPrivilegedApp"
|
|
||||||
android:dependency="expert"/>
|
|
||||||
</android.support.v7.preference.PreferenceCategory>
|
</android.support.v7.preference.PreferenceCategory>
|
||||||
|
|
||||||
</android.support.v7.preference.PreferenceScreen>
|
</android.support.v7.preference.PreferenceScreen>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user