diff --git a/F-Droid-Privileged/.gitignore b/F-Droid-Privileged/.gitignore new file mode 100644 index 000000000..a44cc0f0f --- /dev/null +++ b/F-Droid-Privileged/.gitignore @@ -0,0 +1,33 @@ +#Android specific +bin +gen +obj +lint.xml +local.properties +release.properties +ant.properties +*.class +*.apk + +#Gradle +.gradle +build +gradle.properties + +#Maven +target +pom.xml.* + +#Eclipse +.project +.classpath +.settings +.metadata + +#IntelliJ IDEA +.idea +*.iml + +#Lint output +lint-report.html +lint-report_files/* \ No newline at end of file diff --git a/F-Droid-Privileged/build.gradle b/F-Droid-Privileged/build.gradle new file mode 100644 index 000000000..d702ffd75 --- /dev/null +++ b/F-Droid-Privileged/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'com.android.application' + +dependencies { + compile project(':privileged-api-lib') +} + +android { + compileSdkVersion 22 + buildToolsVersion '23.0.0' + + defaultConfig { + minSdkVersion 8 + targetSdkVersion 22 + versionCode 1001 + versionName "0.1-alpha1" + } + + compileOptions { + compileOptions.encoding = "UTF-8" + + // Use Java 1.7, requires minSdk 8 + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + lintOptions { + // Do not abort build if lint finds errors + abortOnError false + } + +} diff --git a/F-Droid-Privileged/src/main/AndroidManifest.xml b/F-Droid-Privileged/src/main/AndroidManifest.xml new file mode 100644 index 000000000..5602cb3e2 --- /dev/null +++ b/F-Droid-Privileged/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/F-Droid/src/android/content/pm/IPackageDeleteObserver.java b/F-Droid-Privileged/src/main/java/android/content/pm/IPackageDeleteObserver.java similarity index 65% rename from F-Droid/src/android/content/pm/IPackageDeleteObserver.java rename to F-Droid-Privileged/src/main/java/android/content/pm/IPackageDeleteObserver.java index 0c4c6857c..be0d4de81 100644 --- a/F-Droid/src/android/content/pm/IPackageDeleteObserver.java +++ b/F-Droid-Privileged/src/main/java/android/content/pm/IPackageDeleteObserver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Dominik Schürmann + * Copyright (C) 2015 Dominik Schürmann * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,31 +19,36 @@ package android.content.pm; +import android.os.Binder; +import android.os.IBinder; +import android.os.IInterface; +import android.os.Parcel; +import android.os.RemoteException; + /** * Just a non-working implementation of this Stub to satisfy compiler! */ -public interface IPackageDeleteObserver extends android.os.IInterface { +public interface IPackageDeleteObserver extends IInterface { + + abstract class Stub extends Binder implements android.content.pm.IPackageDeleteObserver { - abstract class Stub extends android.os.Binder implements - android.content.pm.IPackageDeleteObserver { public Stub() { throw new RuntimeException("Stub!"); } - public static android.content.pm.IPackageDeleteObserver asInterface(android.os.IBinder obj) { + public static IPackageDeleteObserver asInterface(IBinder obj) { throw new RuntimeException("Stub!"); } - public android.os.IBinder asBinder() { + public IBinder asBinder() { throw new RuntimeException("Stub!"); } - public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, - int flags) throws android.os.RemoteException { + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { throw new RuntimeException("Stub!"); } } - void packageDeleted(java.lang.String packageName, int returnCode) - throws android.os.RemoteException; + void packageDeleted(java.lang.String packageName, int returnCode) throws RemoteException; } diff --git a/F-Droid/src/android/content/pm/IPackageInstallObserver.java b/F-Droid-Privileged/src/main/java/android/content/pm/IPackageInstallObserver.java similarity index 64% rename from F-Droid/src/android/content/pm/IPackageInstallObserver.java rename to F-Droid-Privileged/src/main/java/android/content/pm/IPackageInstallObserver.java index 689198afd..ae5b3ab12 100644 --- a/F-Droid/src/android/content/pm/IPackageInstallObserver.java +++ b/F-Droid-Privileged/src/main/java/android/content/pm/IPackageInstallObserver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Dominik Schürmann + * Copyright (C) 2015 Dominik Schürmann * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,31 +19,36 @@ package android.content.pm; +import android.os.Binder; +import android.os.IBinder; +import android.os.IInterface; +import android.os.Parcel; +import android.os.RemoteException; + /** * Just a non-working implementation of this Stub to satisfy compiler! */ -public interface IPackageInstallObserver extends android.os.IInterface { +public interface IPackageInstallObserver extends IInterface { + + abstract class Stub extends Binder implements android.content.pm.IPackageInstallObserver { - abstract class Stub extends android.os.Binder implements - android.content.pm.IPackageInstallObserver { public Stub() { throw new RuntimeException("Stub!"); } - public static android.content.pm.IPackageInstallObserver asInterface(android.os.IBinder obj) { + public static android.content.pm.IPackageInstallObserver asInterface(IBinder obj) { throw new RuntimeException("Stub!"); } - public android.os.IBinder asBinder() { + public IBinder asBinder() { throw new RuntimeException("Stub!"); } - public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, - int flags) throws android.os.RemoteException { + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { throw new RuntimeException("Stub!"); } } - void packageInstalled(java.lang.String packageName, int returnCode) - throws android.os.RemoteException; + void packageInstalled(String packageName, int returnCode) throws RemoteException; } \ No newline at end of file diff --git a/F-Droid-Privileged/src/main/java/org/fdroid/fdroid/privileged/PrivilegedService.java b/F-Droid-Privileged/src/main/java/org/fdroid/fdroid/privileged/PrivilegedService.java new file mode 100644 index 000000000..ae5d95e53 --- /dev/null +++ b/F-Droid-Privileged/src/main/java/org/fdroid/fdroid/privileged/PrivilegedService.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2015 Dominik Schürmann + * + * 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; + +import android.*; +import android.Manifest; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.pm.IPackageDeleteObserver; +import android.content.pm.IPackageInstallObserver; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.lang.reflect.Method; + +/** + * This service provides an API via AIDL IPC for the main F-Droid app to install/delete packages. + */ +public class PrivilegedService extends Service { + + public static final String TAG = "PrivilegedFDroid"; + + private Method mInstallMethod; + private Method mDeleteMethod; + + private boolean hasPrivilegedPermissionsImpl() { + boolean hasInstallPermission = + (getPackageManager().checkPermission(Manifest.permission.INSTALL_PACKAGES, getPackageName()) + == PackageManager.PERMISSION_GRANTED); + boolean hasDeletePermission = + (getPackageManager().checkPermission(Manifest.permission.DELETE_PACKAGES, getPackageName()) + == PackageManager.PERMISSION_GRANTED); + + return (hasInstallPermission && hasDeletePermission); + } + + private void installPackageImpl(Uri packageURI, int flags, String installerPackageName, + final IPrivilegedCallback callback) { + + // Internal callback from the system + IPackageInstallObserver.Stub installObserver = new IPackageInstallObserver.Stub() { + @Override + public void packageInstalled(String packageName, int returnCode) throws RemoteException { + // forward this internal callback to our callback + try { + callback.handleResult(packageName, returnCode); + } catch (RemoteException e1) { + Log.e(TAG, "RemoteException", e1); + } + } + }; + + // execute internal method + try { + mInstallMethod.invoke(getPackageManager(), packageURI, installObserver, + flags, installerPackageName); + } catch (Exception e) { + Log.e(TAG, "Android not compatible!", e); + try { + callback.handleResult(null, 0); + } catch (RemoteException e1) { + Log.e(TAG, "RemoteException", e1); + } + } + } + + private void deletePackageImpl(String packageName, int flags, final IPrivilegedCallback callback) { + + // Internal callback from the system + IPackageDeleteObserver.Stub deleteObserver = new IPackageDeleteObserver.Stub() { + @Override + public void packageDeleted(String packageName, int returnCode) throws RemoteException { + // forward this internal callback to our callback + try { + callback.handleResult(packageName, returnCode); + } catch (RemoteException e1) { + Log.e(TAG, "RemoteException", e1); + } + } + }; + + // execute internal method + try { + mDeleteMethod.invoke(getPackageManager(), packageName, deleteObserver, flags); + } catch (Exception e) { + Log.e(TAG, "Android not compatible!", e); + try { + callback.handleResult(null, 0); + } catch (RemoteException e1) { + Log.e(TAG, "RemoteException", e1); + } + } + + } + + private final IPrivilegedService.Stub mBinder = new IPrivilegedService.Stub() { + @Override + public boolean hasPrivilegedPermissions() { + return hasPrivilegedPermissionsImpl(); + } + + @Override + public void installPackage(Uri packageURI, int flags, String installerPackageName, + IPrivilegedCallback callback) { + installPackageImpl(packageURI, flags, installerPackageName, callback); + } + + @Override + public void deletePackage(String packageName, int flags, IPrivilegedCallback callback) { + deletePackageImpl(packageName, flags, callback); + } + }; + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + @Override + public void onCreate() { + super.onCreate(); + + // get internal methods via reflection + try { + Class[] installTypes = { + Uri.class, IPackageInstallObserver.class, int.class, + String.class + }; + Class[] deleteTypes = { + String.class, IPackageDeleteObserver.class, + int.class + }; + + PackageManager pm = getPackageManager(); + mInstallMethod = pm.getClass().getMethod("installPackage", installTypes); + mDeleteMethod = pm.getClass().getMethod("deletePackage", deleteTypes); + } catch (NoSuchMethodException e) { + Log.e(TAG, "Android not compatible!", e); + stopSelf(); + } + } + +} diff --git a/F-Droid-Privileged/src/main/res/mipmap-hdpi/ic_launcher.png b/F-Droid-Privileged/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..e34b9df9d Binary files /dev/null and b/F-Droid-Privileged/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/F-Droid-Privileged/src/main/res/mipmap-ldpi/ic_launcher.png b/F-Droid-Privileged/src/main/res/mipmap-ldpi/ic_launcher.png new file mode 100644 index 000000000..4aec4b782 Binary files /dev/null and b/F-Droid-Privileged/src/main/res/mipmap-ldpi/ic_launcher.png differ diff --git a/F-Droid-Privileged/src/main/res/mipmap-mdpi/ic_launcher.png b/F-Droid-Privileged/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..b56588838 Binary files /dev/null and b/F-Droid-Privileged/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/F-Droid-Privileged/src/main/res/mipmap-xhdpi/ic_launcher.png b/F-Droid-Privileged/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..4927a9d7e Binary files /dev/null and b/F-Droid-Privileged/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/F-Droid-Privileged/src/main/res/mipmap-xxhdpi/ic_launcher.png b/F-Droid-Privileged/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..bf34082d3 Binary files /dev/null and b/F-Droid-Privileged/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/F-Droid-Privileged/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/F-Droid-Privileged/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..246c8094f Binary files /dev/null and b/F-Droid-Privileged/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/F-Droid-Privileged/src/main/res/values/strings.xml b/F-Droid-Privileged/src/main/res/values/strings.xml new file mode 100644 index 000000000..50777a72c --- /dev/null +++ b/F-Droid-Privileged/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + F-Droid Privileged Extension + + diff --git a/F-Droid/AndroidManifest.xml b/F-Droid/AndroidManifest.xml index f2683b0a6..acc024d3e 100644 --- a/F-Droid/AndroidManifest.xml +++ b/F-Droid/AndroidManifest.xml @@ -51,11 +51,7 @@ android:maxSdkVersion="18" /> - - - + + android:name=".privileged.install.InstallPrivilegedBootReceiver" > diff --git a/F-Droid/build.gradle b/F-Droid/build.gradle index 6d6aa115e..ef1fc2593 100644 --- a/F-Droid/build.gradle +++ b/F-Droid/build.gradle @@ -12,6 +12,7 @@ if (!hasProperty('sourceDeps')) { } dependencies { + compile project(':privileged-api-lib') compile 'com.android.support:support-v4:22.2.1' compile 'com.android.support:appcompat-v7:22.1.1' @@ -59,6 +60,8 @@ if (!hasProperty('sourceDeps')) { compile 'com.android.support:appcompat-v7:22.1.1' compile 'com.android.support:support-annotations:22.2.1' + compile project(':privileged-api-lib') + compile 'org.thoughtcrime.ssl.pinning:AndroidPinning:1.0.0' compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.4' compile 'com.google.zxing:core:3.2.1' diff --git a/F-Droid/res/layout-v11/app_permission_item.xml b/F-Droid/res/layout-v11/app_permission_item.xml index b5aab370d..0cb5e3405 100644 --- a/F-Droid/res/layout-v11/app_permission_item.xml +++ b/F-Droid/res/layout-v11/app_permission_item.xml @@ -18,7 +18,7 @@ Defines the layout of a single permission item. --> - - - - @@ -50,4 +50,4 @@ This is the structure for the list of all permissions. android:text="@string/devicePerms" /> - + diff --git a/F-Droid/res/values/strings.xml b/F-Droid/res/values/strings.xml index 5326ce92c..319f1a2d0 100644 --- a/F-Droid/res/values/strings.xml +++ b/F-Droid/res/values/strings.xml @@ -28,7 +28,7 @@ Enable privileged F-Droid Use privileged permissions to install, update, and remove packages Uninstall privileged F-Droid - Uninstall F-Droid when installed as a privileged app + Uninstalls the additional privileged F-Droid app Name of your Local Repo The advertised title of your local repo: %s Use Private Connection @@ -296,23 +296,23 @@ An error occurred while parsing the package Uninstall error Failed to uninstall due to an unknown error - System permissions denied - This option is only available when F-Droid is installed as a privileged app. - Install as a privileged app + Privileged F-Droid is not available + This option is only available when privileged F-Droid is installed. + Install privileged F-Droid Install privileged F-Droid? Touch for more information. - Touch to install F-Droid as a privileged app, tightly coupled with the Android operating system. This allows F-Droid to install, upgrade and uninstall apps on its own.\nYou can also do this later from F-Droid\'s preferences. - Successful installation as a privileged app - Installation as a privileged app failed - F-Droid has been successfully installed as a privileged app. This allows F-Droid to install, upgrade and uninstall apps on its own. - The installation of F-Droid as a privileged app failed. The installation method is not supported by all Android distributions, please consult the F-Droid bug tracker for more information. + Touch to install privileged F-Droid, tightly coupled with the Android operating system. This allows F-Droid to install, upgrade and uninstall apps on its own.\nYou can also do this later from F-Droid\'s preferences. + Successfully installed privileged F-Droid + Installation of privileged F-Droid failed + Privileged F-Droid has been successfully installed. This allows F-Droid to install, upgrade and uninstall apps on its own. + The installation of privileged F-Droid has failed. The installation method is not supported by all Android distributions, please consult the F-Droid bug tracker for more information. installing… uninstalling… - Do you want to install F-Droid as a privileged app?\nThis takes up to 10 seconds where <b>no user interface</b> is shown. - Do you want to install F-Droid as a privileged app?\nThis takes up to 10 seconds where <b>no user interface</b> is shown and the device will be <b>rebooted</b> afterwards. - Looks like you have root access on your device. You can now install F-Droid as a privileged app, tightly coupled with the Android operating system. This allows F-Droid to install, upgrade and uninstall apps on its own. - Do you want to uninstall F-Droid? - This will uninstall F-Droid completely. + Do you want to install privileged F-Droid?\nThis takes up to 10 seconds. + Do you want to install privileged F-Droid?\nThis takes up to 10 seconds and the device will be <b>rebooted</b> afterwards. + Looks like you have root access on your device. You can now install privileged F-Droid, tightly coupled with the Android operating system. This allows F-Droid to install, upgrade and uninstall apps on its own. + Do you want to uninstall privileged F-Droid? + This will uninstall privileged F-Droid. Uninstall F-Droid is an installable catalogue of FOSS (Free and Open Source Software) applications for the Android platform. The client makes it easy to browse, install, and keep track of updates on your device. diff --git a/F-Droid/res/xml/preferences.xml b/F-Droid/res/xml/preferences.xml index 708ed5150..e038d5963 100644 --- a/F-Droid/res/xml/preferences.xml +++ b/F-Droid/res/xml/preferences.xml @@ -75,11 +75,11 @@ android:key="expert" /> diff --git a/F-Droid/src/org/fdroid/fdroid/AppDetails.java b/F-Droid/src/org/fdroid/fdroid/AppDetails.java index a5c7e02a4..a5c1aa5a1 100644 --- a/F-Droid/src/org/fdroid/fdroid/AppDetails.java +++ b/F-Droid/src/org/fdroid/fdroid/AppDetails.java @@ -892,7 +892,7 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A private void installApk(File file) { try { - installer.installPackage(file); + installer.installPackage(file, app.id); } catch (AndroidNotCompatibleException e) { Log.e(TAG, "Android not compatible with this Installer!", e); } diff --git a/F-Droid/src/org/fdroid/fdroid/FDroid.java b/F-Droid/src/org/fdroid/fdroid/FDroid.java index b78c537c0..a5926a726 100644 --- a/F-Droid/src/org/fdroid/fdroid/FDroid.java +++ b/F-Droid/src/org/fdroid/fdroid/FDroid.java @@ -44,7 +44,7 @@ import android.widget.Toast; import org.fdroid.fdroid.compat.TabManager; import org.fdroid.fdroid.data.AppProvider; import org.fdroid.fdroid.data.NewRepoConfig; -import org.fdroid.fdroid.installer.InstallIntoSystemDialogActivity; +import org.fdroid.fdroid.privileged.install.InstallPrivilegedDialogActivity; import org.fdroid.fdroid.views.AppListFragmentPagerAdapter; import org.fdroid.fdroid.views.ManageReposActivity; import org.fdroid.fdroid.views.swap.SwapWorkflowActivity; @@ -98,7 +98,7 @@ public class FDroid extends ActionBarActivity { Uri uri = AppProvider.getContentUri(); getContentResolver().registerContentObserver(uri, true, new AppObserver()); - InstallIntoSystemDialogActivity.firstTime(this); + InstallPrivilegedDialogActivity.firstTime(this); if (UpdateService.isNetworkAvailableForUpdate(this)) { UpdateService.updateNow(this); diff --git a/F-Droid/src/org/fdroid/fdroid/Preferences.java b/F-Droid/src/org/fdroid/fdroid/Preferences.java index 1dd04f0a3..2b004d13c 100644 --- a/F-Droid/src/org/fdroid/fdroid/Preferences.java +++ b/F-Droid/src/org/fdroid/fdroid/Preferences.java @@ -49,8 +49,8 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi public static final String PREF_CACHE_APK = "cacheDownloaded"; public static final String PREF_EXPERT = "expert"; public static final String PREF_UPD_LAST = "lastUpdateCheck"; - public static final String PREF_SYSTEM_INSTALLER = "systemInstaller"; - public static final String PREF_UNINSTALL_SYSTEM_APP = "uninstallSystemApp"; + 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_HTTPS = "localRepoHttps"; public static final String PREF_LANGUAGE = "language"; @@ -59,12 +59,12 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi public static final String PREF_PROXY_PORT = "proxyPort"; public static final String PREF_SHOW_NFC_DURING_SWAP = "showNfcDuringSwap"; public static final String PREF_FIRST_TIME = "firstTime"; - public static final String PREF_POST_SYSTEM_INSTALL = "postSystemInstall"; + public static final String PREF_POST_PRIVILEGED_INSTALL = "postPrivilegedInstall"; private static final boolean DEFAULT_COMPACT_LAYOUT = false; private static final boolean DEFAULT_ROOTED = true; private static final int DEFAULT_UPD_HISTORY = 14; - private static final boolean DEFAULT_SYSTEM_INSTALLER = false; + private static final boolean DEFAULT_PRIVILEGED_INSTALLER = false; private static final boolean DEFAULT_LOCAL_REPO_BONJOUR = true; private static final boolean DEFAULT_CACHE_APK = false; private static final boolean DEFAULT_LOCAL_REPO_HTTPS = false; @@ -76,7 +76,7 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi public static final int DEFAULT_PROXY_PORT = 8118; public static final boolean DEFAULT_SHOW_NFC_DURING_SWAP = true; private static final boolean DEFAULT_FIRST_TIME = true; - private static final boolean DEFAULT_POST_SYSTEM_INSTALL = false; + private static final boolean DEFAULT_POST_PRIVILEGED_INSTALL = false; private boolean compactLayout = DEFAULT_COMPACT_LAYOUT; private boolean filterAppsRequiringRoot = DEFAULT_ROOTED; @@ -101,12 +101,12 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi initialized.put(key, false); } - public boolean isSystemInstallerEnabled() { - return preferences.getBoolean(PREF_SYSTEM_INSTALLER, DEFAULT_SYSTEM_INSTALLER); + public boolean isPrivilegedInstallerEnabled() { + return preferences.getBoolean(PREF_PRIVILEGED_INSTALLER, DEFAULT_PRIVILEGED_INSTALLER); } - public void setSystemInstallerEnabled(boolean enable) { - preferences.edit().putBoolean(PREF_SYSTEM_INSTALLER, enable).commit(); + public void setPrivilegedInstallerEnabled(boolean enable) { + preferences.edit().putBoolean(PREF_PRIVILEGED_INSTALLER, enable).commit(); } public boolean isFirstTime() { @@ -117,12 +117,12 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi preferences.edit().putBoolean(PREF_FIRST_TIME, firstTime).commit(); } - public boolean isPostSystemInstall() { - return preferences.getBoolean(PREF_POST_SYSTEM_INSTALL, DEFAULT_POST_SYSTEM_INSTALL); + public boolean isPostPrivilegedInstall() { + return preferences.getBoolean(PREF_POST_PRIVILEGED_INSTALL, DEFAULT_POST_PRIVILEGED_INSTALL); } - public void setPostSystemInstall(boolean postInstall) { - preferences.edit().putBoolean(PREF_POST_SYSTEM_INSTALL, postInstall).commit(); + public void setPostPrivilegedInstall(boolean postInstall) { + preferences.edit().putBoolean(PREF_POST_PRIVILEGED_INSTALL, postInstall).commit(); } public boolean shouldCacheApks() { diff --git a/F-Droid/src/org/fdroid/fdroid/installer/DefaultInstallerSdk14.java b/F-Droid/src/org/fdroid/fdroid/installer/DefaultSdk14Installer.java similarity index 97% rename from F-Droid/src/org/fdroid/fdroid/installer/DefaultInstallerSdk14.java rename to F-Droid/src/org/fdroid/fdroid/installer/DefaultSdk14Installer.java index 3373e65ef..ba64aaedb 100644 --- a/F-Droid/src/org/fdroid/fdroid/installer/DefaultInstallerSdk14.java +++ b/F-Droid/src/org/fdroid/fdroid/installer/DefaultSdk14Installer.java @@ -39,10 +39,10 @@ import java.util.List; * unattended installations. */ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) -public class DefaultInstallerSdk14 extends Installer { +public class DefaultSdk14Installer extends Installer { private final Activity mActivity; - public DefaultInstallerSdk14(Activity activity, PackageManager pm, InstallerCallback callback) + public DefaultSdk14Installer(Activity activity, PackageManager pm, InstallerCallback callback) throws AndroidNotCompatibleException { super(activity, pm, callback); this.mActivity = activity; diff --git a/F-Droid/src/org/fdroid/fdroid/installer/Installer.java b/F-Droid/src/org/fdroid/fdroid/installer/Installer.java index de9c8ff6f..295032086 100644 --- a/F-Droid/src/org/fdroid/fdroid/installer/Installer.java +++ b/F-Droid/src/org/fdroid/fdroid/installer/Installer.java @@ -19,7 +19,6 @@ package org.fdroid.fdroid.installer; -import android.Manifest.permission; import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -28,6 +27,7 @@ import android.util.Log; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.Utils; +import org.fdroid.fdroid.privileged.install.InstallPrivilegedDialogActivity; import java.io.File; import java.util.List; @@ -107,13 +107,13 @@ abstract public class Installer { InstallerCallback callback) { // system permissions and pref enabled -> SystemInstaller - boolean isSystemInstallerEnabled = Preferences.get().isSystemInstallerEnabled(); + boolean isSystemInstallerEnabled = Preferences.get().isPrivilegedInstallerEnabled(); if (isSystemInstallerEnabled) { - if (hasSystemPermissions(activity, pm)) { + if (PrivilegedInstaller.isAvailable(activity)) { Utils.DebugLog(TAG, "system permissions -> SystemInstaller"); try { - return new SystemInstaller(activity, pm, callback); + return new PrivilegedInstaller(activity, pm, callback); } catch (AndroidNotCompatibleException e) { Log.e(TAG, "Android not compatible with SystemInstaller!", e); } @@ -128,7 +128,7 @@ abstract public class Installer { try { Utils.DebugLog(TAG, "try default installer for Android >= 4"); - return new DefaultInstallerSdk14(activity, pm, callback); + return new DefaultSdk14Installer(activity, pm, callback); } catch (AndroidNotCompatibleException e) { Log.e(TAG, "Android not compatible with DefaultInstallerSdk14!", e); } @@ -147,24 +147,30 @@ abstract public class Installer { return null; } - public static boolean hasSystemPermissions(Context context, PackageManager pm) { - boolean hasInstallPermission = - (pm.checkPermission(permission.INSTALL_PACKAGES, context.getPackageName()) - == PackageManager.PERMISSION_GRANTED); - boolean hasDeletePermission = - (pm.checkPermission(permission.DELETE_PACKAGES, context.getPackageName()) - == PackageManager.PERMISSION_GRANTED); - - return (hasInstallPermission && hasDeletePermission); - } - - public void installPackage(File apkFile) throws AndroidNotCompatibleException { + public void installPackage(File apkFile, String packageName) throws AndroidNotCompatibleException { // check if file exists... if (!apkFile.exists()) { Log.e(TAG, "Couldn't find file " + apkFile + " to install."); return; } + // special case: Install F-Droid Privileged + if (packageName.equals(PrivilegedInstaller.PRIVILEGED_PACKAGE_NAME)) { + Activity activity; + try { + activity = (Activity) mContext; + } catch (ClassCastException e) { + Log.d(TAG, "F-Droid Privileged can only be updated using an activity!"); + return; + } + + Intent installIntent = new Intent(activity, InstallPrivilegedDialogActivity.class); + installIntent.setAction(InstallPrivilegedDialogActivity.ACTION_INSTALL); + installIntent.putExtra(InstallPrivilegedDialogActivity.EXTRA_INSTALL_APK, apkFile.getAbsolutePath()); + activity.startActivity(installIntent); + return; + } + installPackageInternal(apkFile); } diff --git a/F-Droid/src/org/fdroid/fdroid/installer/SystemInstaller.java b/F-Droid/src/org/fdroid/fdroid/installer/PrivilegedInstaller.java similarity index 76% rename from F-Droid/src/org/fdroid/fdroid/installer/SystemInstaller.java rename to F-Droid/src/org/fdroid/fdroid/installer/PrivilegedInstaller.java index da779c52a..132a2e5fe 100644 --- a/F-Droid/src/org/fdroid/fdroid/installer/SystemInstaller.java +++ b/F-Droid/src/org/fdroid/fdroid/installer/PrivilegedInstaller.java @@ -21,22 +21,29 @@ package org.fdroid.fdroid.installer; import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageDeleteObserver; -import android.content.pm.IPackageInstallObserver; import android.content.pm.PackageManager; import android.net.Uri; +import android.os.Bundle; +import android.os.IBinder; import android.os.RemoteException; import android.support.v7.app.AlertDialog; import android.util.Log; import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; +import org.fdroid.fdroid.privileged.IPrivilegedCallback; +import org.fdroid.fdroid.privileged.IPrivilegedService; +import org.fdroid.fdroid.privileged.views.AppDiff; +import org.fdroid.fdroid.privileged.views.AppSecurityPermissions; +import org.fdroid.fdroid.privileged.views.InstallConfirmActivity; import java.io.File; -import java.lang.reflect.Method; import java.util.List; /** @@ -63,78 +70,69 @@ import java.util.List; * https://android.googlesource.com/platform * /frameworks/base/+/ccbf84f44c9e6a5ed3c08673614826bb237afc54 */ -public class SystemInstaller extends Installer { +public class PrivilegedInstaller extends Installer { - private static final String TAG = "SystemInstaller"; + private static final String TAG = "PrivilegedInstaller"; + + private static final String PRIVILEGED_SERVICE_INTENT = "org.fdroid.fdroid.privileged.IPrivilegedService"; + public static final String PRIVILEGED_PACKAGE_NAME = "org.fdroid.fdroid.privileged"; private Activity mActivity; - private final PackageInstallObserver mInstallObserver; - private final PackageDeleteObserver mDeleteObserver; - private Method mInstallMethod; - private Method mDeleteMethod; public static final int REQUEST_CONFIRM_PERMS = 0; - public SystemInstaller(Activity activity, PackageManager pm, - InstallerCallback callback) throws AndroidNotCompatibleException { + public PrivilegedInstaller(Activity activity, PackageManager pm, + InstallerCallback callback) throws AndroidNotCompatibleException { super(activity, pm, callback); this.mActivity = activity; + } - // create internal callbacks - mInstallObserver = new PackageInstallObserver(); - mDeleteObserver = new PackageDeleteObserver(); + public static boolean isAvailable(Context context) { + // check if installed + PackageManager pm = context.getPackageManager(); try { - Class[] installTypes = { - Uri.class, IPackageInstallObserver.class, int.class, - String.class - }; - Class[] deleteTypes = { - String.class, IPackageDeleteObserver.class, - int.class - }; - - mInstallMethod = mPm.getClass().getMethod("installPackage", installTypes); - mDeleteMethod = mPm.getClass().getMethod("deletePackage", deleteTypes); - } catch (NoSuchMethodException e) { - throw new AndroidNotCompatibleException(e); + pm.getPackageInfo(PRIVILEGED_PACKAGE_NAME, PackageManager.GET_ACTIVITIES); + } catch (PackageManager.NameNotFoundException e) { + return false; } - } - /** - * Internal install callback from the system - */ - class PackageInstallObserver extends IPackageInstallObserver.Stub { - public void packageInstalled(String packageName, int returnCode) throws RemoteException { - // TODO: propagate other return codes? - if (returnCode == INSTALL_SUCCEEDED) { - Utils.DebugLog(TAG, "Install succeeded"); + // check if it has the privileged permissions granted + final Object mutex = new Object(); + final Bundle returnBundle = new Bundle(); + ServiceConnection mServiceConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + IPrivilegedService privService = IPrivilegedService.Stub.asInterface(service); - mCallback.onSuccess(InstallerCallback.OPERATION_INSTALL); - } else { - Log.e(TAG, "Install failed with returnCode " + returnCode); - mCallback.onError(InstallerCallback.OPERATION_INSTALL, - InstallerCallback.ERROR_CODE_OTHER); + try { + boolean hasPermissions = privService.hasPrivilegedPermissions(); + returnBundle.putBoolean("has_permissions", hasPermissions); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException", e); + } + + synchronized (mutex) { + mutex.notify(); + } + } + + public void onServiceDisconnected(ComponentName name) { + } + }; + Intent serviceIntent = new Intent(PRIVILEGED_SERVICE_INTENT); + serviceIntent.setPackage(PRIVILEGED_PACKAGE_NAME); + context.getApplicationContext().bindService(serviceIntent, mServiceConnection, + Context.BIND_AUTO_CREATE); + + synchronized (mutex) { + try { + mutex.wait(3000); + } catch (InterruptedException e) { + // don't care } } - } - /** - * Internal delete callback from the system - */ - class PackageDeleteObserver extends IPackageDeleteObserver.Stub { - public void packageDeleted(String packageName, int returnCode) throws RemoteException { - // TODO: propagate other return codes? - if (returnCode == DELETE_SUCCEEDED) { - Utils.DebugLog(TAG, "Delete succeeded"); - - mCallback.onSuccess(InstallerCallback.OPERATION_DELETE); - } else { - Log.e(TAG, "Delete failed with returnCode " + returnCode); - mCallback.onError(InstallerCallback.OPERATION_DELETE, - InstallerCallback.ERROR_CODE_OTHER); - } - } + return (returnBundle.getBoolean("has_permission", false)); } @Override @@ -176,15 +174,47 @@ public class SystemInstaller extends Installer { return 1; } - private void doInstallPackageInternal(Uri packageURI) throws AndroidNotCompatibleException { - try { - mInstallMethod.invoke(mPm, packageURI, mInstallObserver, - INSTALL_REPLACE_EXISTING, null); - } catch (Exception e) { - throw new AndroidNotCompatibleException(e); - } - } + private void doInstallPackageInternal(final Uri packageURI) throws AndroidNotCompatibleException { + ServiceConnection mServiceConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + IPrivilegedService privService = IPrivilegedService.Stub.asInterface(service); + IPrivilegedCallback callback = new IPrivilegedCallback() { + @Override + public void handleResult(String packageName, int returnCode) throws RemoteException { + // TODO: propagate other return codes? + if (returnCode == INSTALL_SUCCEEDED) { + Utils.DebugLog(TAG, "Install succeeded"); + mCallback.onSuccess(InstallerCallback.OPERATION_INSTALL); + } else { + Log.e(TAG, "Install failed with returnCode " + returnCode); + mCallback.onError(InstallerCallback.OPERATION_INSTALL, + InstallerCallback.ERROR_CODE_OTHER); + } + } + + @Override + public IBinder asBinder() { + return null; + } + }; + + try { + privService.installPackage(packageURI, INSTALL_REPLACE_EXISTING, null, callback); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException", e); + } + } + + public void onServiceDisconnected(ComponentName name) { + } + }; + + Intent serviceIntent = new Intent(PRIVILEGED_SERVICE_INTENT); + serviceIntent.setPackage(PRIVILEGED_PACKAGE_NAME); + mContext.getApplicationContext().bindService(serviceIntent, mServiceConnection, + Context.BIND_AUTO_CREATE); + } @Override protected void installPackageInternal(List apkFiles) @@ -250,35 +280,70 @@ public class SystemInstaller extends Installer { private void doDeletePackageInternal(final String packageName) throws AndroidNotCompatibleException { - try { - mDeleteMethod.invoke(mPm, packageName, mDeleteObserver, 0); - } catch (Exception e) { - throw new AndroidNotCompatibleException(e); - } + ServiceConnection mServiceConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + IPrivilegedService privService = IPrivilegedService.Stub.asInterface(service); + + IPrivilegedCallback callback = new IPrivilegedCallback() { + @Override + public void handleResult(String packageName, int returnCode) throws RemoteException { + // TODO: propagate other return codes? + if (returnCode == DELETE_SUCCEEDED) { + Utils.DebugLog(TAG, "Delete succeeded"); + + mCallback.onSuccess(InstallerCallback.OPERATION_DELETE); + } else { + Log.e(TAG, "Delete failed with returnCode " + returnCode); + mCallback.onError(InstallerCallback.OPERATION_DELETE, + InstallerCallback.ERROR_CODE_OTHER); + } + } + + @Override + public IBinder asBinder() { + return null; + } + }; + + try { + privService.deletePackage(packageName, 0, callback); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException", e); + } + } + + public void onServiceDisconnected(ComponentName name) { + } + }; + + Intent serviceIntent = new Intent(PRIVILEGED_SERVICE_INTENT); + serviceIntent.setPackage(PRIVILEGED_PACKAGE_NAME); + mContext.getApplicationContext().bindService(serviceIntent, mServiceConnection, + Context.BIND_AUTO_CREATE); } @Override public boolean handleOnActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { - case REQUEST_CONFIRM_PERMS: - if (resultCode == Activity.RESULT_OK) { - final Uri packageUri = data.getData(); - try { - doInstallPackageInternal(packageUri); - } catch (AndroidNotCompatibleException e) { + case REQUEST_CONFIRM_PERMS: + if (resultCode == Activity.RESULT_OK) { + final Uri packageUri = data.getData(); + try { + doInstallPackageInternal(packageUri); + } catch (AndroidNotCompatibleException e) { + mCallback.onError(InstallerCallback.OPERATION_INSTALL, + InstallerCallback.ERROR_CODE_OTHER); + } + } else if (resultCode == InstallConfirmActivity.RESULT_CANNOT_PARSE) { mCallback.onError(InstallerCallback.OPERATION_INSTALL, - InstallerCallback.ERROR_CODE_OTHER); + InstallerCallback.ERROR_CODE_CANNOT_PARSE); + } else { // Activity.RESULT_CANCELED + mCallback.onError(InstallerCallback.OPERATION_INSTALL, + InstallerCallback.ERROR_CODE_CANCELED); } - } else if (resultCode == InstallConfirmActivity.RESULT_CANNOT_PARSE) { - mCallback.onError(InstallerCallback.OPERATION_INSTALL, - InstallerCallback.ERROR_CODE_CANNOT_PARSE); - } else { // Activity.RESULT_CANCELED - mCallback.onError(InstallerCallback.OPERATION_INSTALL, - InstallerCallback.ERROR_CODE_CANCELED); - } - return true; - default: - return false; + return true; + default: + return false; } } @@ -579,7 +644,7 @@ public class SystemInstaller extends Installer { * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} * if the system failed to install the package because it is attempting to define a * permission that is already defined by some existing package. - * + *

*

The package name of the app which has already defined the permission is passed to * a {@link PackageInstallObserver}, if any, as the {@link #EXTRA_EXISTING_PACKAGE} * string extra; and the name of the permission being redefined is passed in the diff --git a/F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystem.java b/F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivileged.java similarity index 77% rename from F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystem.java rename to F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivileged.java index e7fd19643..181244071 100644 --- a/F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystem.java +++ b/F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivileged.java @@ -17,13 +17,14 @@ * MA 02110-1301, USA. */ -package org.fdroid.fdroid.installer; +package org.fdroid.fdroid.privileged.install; import android.content.Context; import android.os.Build; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.R; +import org.fdroid.fdroid.installer.PrivilegedInstaller; import java.util.ArrayList; import java.util.List; @@ -35,15 +36,17 @@ import eu.chainfire.libsuperuser.Shell; * 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 */ -abstract class InstallIntoSystem { +abstract class InstallPrivileged { protected final Context context; - public InstallIntoSystem(final Context context) { + private static final String APK_FILE_NAME = "FDroidPrivileged.apk"; + + public InstallPrivileged(final Context context) { this.context = context; } - public static InstallIntoSystem create(final Context context) { + public static InstallPrivileged create(final Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { return new LollipopImpl(context); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { @@ -65,10 +68,10 @@ abstract class InstallIntoSystem { final void runUninstall() { final String[] commands = { - "am force-stop org.fdroid.fdroid", - "pm clear org.fdroid.fdroid", + "am force-stop " + PrivilegedInstaller.PRIVILEGED_PACKAGE_NAME, + "pm clear " + PrivilegedInstaller.PRIVILEGED_PACKAGE_NAME, "mount -o rw,remount /system", - "pm uninstall " + context.getPackageName(), + "pm uninstall " + PrivilegedInstaller.PRIVILEGED_PACKAGE_NAME, "rm -f " + getInstallPath(), "sleep 5", "mount -o ro,remount /system" @@ -76,32 +79,32 @@ abstract class InstallIntoSystem { Shell.SU.run(commands); } - final void runInstall() { + final void runInstall(String apkPath) { onPreInstall(); - Shell.SU.run(getInstallCommands()); + Shell.SU.run(getInstallCommands(apkPath)); } protected String getInstallPath() { - return getSystemFolder() + "FDroid.apk"; + return getSystemFolder() + APK_FILE_NAME; } - private List getInstallCommands() { + private List getInstallCommands(String apkPath) { final List commands = new ArrayList<>(); commands.add("mount -o rw,remount /system"); - commands.addAll(getCopyToSystemCommands()); - commands.add("pm uninstall -k " + context.getPackageName()); // -k to retain data + commands.addAll(getCopyToSystemCommands(apkPath)); + commands.add("pm uninstall " + PrivilegedInstaller.PRIVILEGED_PACKAGE_NAME); commands.add("mv " + getInstallPath() + ".tmp " + getInstallPath()); commands.add("pm install -r " + getInstallPath()); commands.add("sleep 5"); // wait until the app is really installed commands.add("mount -o ro,remount /system"); - commands.add("am force-stop org.fdroid.fdroid"); + commands.add("am force-stop " + PrivilegedInstaller.PRIVILEGED_PACKAGE_NAME); commands.addAll(getPostInstallCommands()); return commands; } - protected List getCopyToSystemCommands() { + protected List getCopyToSystemCommands(String apkPath) { final List commands = new ArrayList<>(2); - commands.add("cat " + context.getPackageCodePath() + " > " + getInstallPath() + ".tmp"); + commands.add("cat " + apkPath + " > " + getInstallPath() + ".tmp"); commands.add("chmod 644 " + getInstallPath() + ".tmp"); return commands; } @@ -112,7 +115,7 @@ abstract class InstallIntoSystem { return commands; } - private static class PreKitKatImpl extends InstallIntoSystem { + private static class PreKitKatImpl extends InstallPrivileged { public PreKitKatImpl(Context context) { super(context); @@ -125,7 +128,7 @@ abstract class InstallIntoSystem { } - private static class KitKatToLollipopImpl extends InstallIntoSystem { + private static class KitKatToLollipopImpl extends InstallPrivileged { public KitKatToLollipopImpl(Context context) { super(context); @@ -146,7 +149,7 @@ abstract class InstallIntoSystem { * 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 InstallIntoSystem { + private static class LollipopImpl extends InstallPrivileged { public LollipopImpl(Context context) { super(context); @@ -155,7 +158,7 @@ abstract class InstallIntoSystem { @Override protected void onPreInstall() { // Setup preference to execute postInstall after reboot - Preferences.get().setPostSystemInstall(true); + Preferences.get().setPostPrivilegedInstall(true); } public String getWarningInfo() { @@ -167,24 +170,24 @@ abstract class InstallIntoSystem { */ @Override protected String getSystemFolder() { - return "/system/priv-app/FDroid/"; + return "/system/priv-app/FDroidPrivileged/"; } /** * Create app directory */ @Override - protected List getCopyToSystemCommands() { + protected List getCopyToSystemCommands(String apkPath) { List commands = new ArrayList<>(3); commands.add("mkdir -p " + getSystemFolder()); // create app directory if not existing commands.add("chmod 755 " + getSystemFolder()); - commands.add("cat " + context.getPackageCodePath() + " > " + getInstallPath() + ".tmp"); + commands.add("cat " + apkPath + " > " + getInstallPath() + ".tmp"); commands.add("chmod 644 " + getInstallPath() + ".tmp"); return commands; } /** - * TODO: Currently only works with reboot + * 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 diff --git a/F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystemBootReceiver.java b/F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivilegedBootReceiver.java similarity index 78% rename from F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystemBootReceiver.java rename to F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivilegedBootReceiver.java index 42f7e57cc..9a1cc0173 100644 --- a/F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystemBootReceiver.java +++ b/F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivilegedBootReceiver.java @@ -17,7 +17,7 @@ * MA 02110-1301, USA. */ -package org.fdroid.fdroid.installer; +package org.fdroid.fdroid.privileged.install; import android.content.BroadcastReceiver; import android.content.Context; @@ -25,16 +25,16 @@ import android.content.Intent; import org.fdroid.fdroid.Preferences; -public class InstallIntoSystemBootReceiver extends BroadcastReceiver { +public class InstallPrivilegedBootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { - if (Preferences.get().isPostSystemInstall()) { - Preferences.get().setPostSystemInstall(false); + if (Preferences.get().isPostPrivilegedInstall()) { + Preferences.get().setPostPrivilegedInstall(false); - Intent postInstall = new Intent(context.getApplicationContext(), InstallIntoSystemDialogActivity.class); - postInstall.setAction(InstallIntoSystemDialogActivity.ACTION_POST_INSTALL); + Intent postInstall = new Intent(context.getApplicationContext(), InstallPrivilegedDialogActivity.class); + postInstall.setAction(InstallPrivilegedDialogActivity.ACTION_POST_INSTALL); postInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(postInstall); } diff --git a/F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystemDialogActivity.java b/F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivilegedDialogActivity.java similarity index 85% rename from F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystemDialogActivity.java rename to F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivilegedDialogActivity.java index 8fe2b102c..2fd978546 100644 --- a/F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystemDialogActivity.java +++ b/F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivilegedDialogActivity.java @@ -17,7 +17,7 @@ * MA 02110-1301, USA. */ -package org.fdroid.fdroid.installer; +package org.fdroid.fdroid.privileged.install; import android.app.Activity; import android.app.Notification; @@ -36,26 +36,33 @@ import android.text.Html; import android.util.Log; import android.view.ContextThemeWrapper; +import org.fdroid.fdroid.AppDetails; import org.fdroid.fdroid.FDroid; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.R; +import org.fdroid.fdroid.installer.PrivilegedInstaller; + +import java.io.File; import eu.chainfire.libsuperuser.Shell; /** * Note: This activity has no view on its own, it displays consecutive dialogs. */ -public class InstallIntoSystemDialogActivity extends FragmentActivity { +public class InstallPrivilegedDialogActivity extends FragmentActivity { private static final String TAG = "InstallIntoSystem"; public static final String ACTION_INSTALL = "install"; + public static final String EXTRA_INSTALL_APK = "apk_file"; + public static final String ACTION_UNINSTALL = "uninstall"; public static final String ACTION_POST_INSTALL = "post_install"; public static final String ACTION_FIRST_TIME = "first_time"; String action; + String apkFile; @Override protected void onCreate(Bundle savedInstanceState) { @@ -69,6 +76,8 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { return; } + apkFile = getIntent().getStringExtra(EXTRA_INSTALL_APK); + action = getIntent().getAction(); if (ACTION_UNINSTALL.equals(action)) { uninstall(); @@ -85,8 +94,8 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { if (Preferences.get().isFirstTime()) { Preferences.get().setFirstTime(false); - if (Installer.hasSystemPermissions(context, context.getPackageManager())) { - Preferences.get().setSystemInstallerEnabled(true); + if (PrivilegedInstaller.isAvailable(context)) { + Preferences.get().setPrivilegedInstallerEnabled(true); } else { runFirstTime(context); } @@ -112,8 +121,8 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { if (false && probablyRoot) { // looks like we have root, at least su has a version number and is present - Intent installIntent = new Intent(context, InstallIntoSystemDialogActivity.class); - installIntent.setAction(InstallIntoSystemDialogActivity.ACTION_FIRST_TIME); + Intent installIntent = new Intent(context, InstallPrivilegedDialogActivity.class); + installIntent.setAction(InstallPrivilegedDialogActivity.ACTION_FIRST_TIME); installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent resultPendingIntent = @@ -157,21 +166,26 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { // hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId()); - String message = getString(R.string.system_install_first_time_message) + "

" + InstallIntoSystem.create(getApplicationContext()).getWarningInfo(); + String message = getString(R.string.system_install_first_time_message) + "

" + + InstallPrivileged.create(getApplicationContext()).getWarningInfo(); AlertDialog.Builder builder = new AlertDialog.Builder(theme) .setMessage(Html.fromHtml(message)) .setPositiveButton(R.string.system_permission_install_via_root, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { - installTask.execute(); + // Open details of F-Droid Privileged + Intent intent = new Intent(InstallPrivilegedDialogActivity.this, AppDetails.class); + intent.putExtra(AppDetails.EXTRA_APPID, + PrivilegedInstaller.PRIVILEGED_PACKAGE_NAME); + startActivity(intent); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { - InstallIntoSystemDialogActivity.this.setResult(Activity.RESULT_CANCELED); - InstallIntoSystemDialogActivity.this.finish(); + InstallPrivilegedDialogActivity.this.setResult(Activity.RESULT_CANCELED); + InstallPrivilegedDialogActivity.this.finish(); } }); builder.create().show(); @@ -188,7 +202,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { super.onPreExecute(); // hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay - ContextThemeWrapper theme = new ContextThemeWrapper(InstallIntoSystemDialogActivity.this, + ContextThemeWrapper theme = new ContextThemeWrapper(InstallPrivilegedDialogActivity.this, FDroidApp.getCurThemeResId()); mProgressDialog = new ProgressDialog(theme); @@ -224,7 +238,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { if (!ACTION_FIRST_TIME.equals(action)) { // hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay - ContextThemeWrapper theme = new ContextThemeWrapper(InstallIntoSystemDialogActivity.this, + ContextThemeWrapper theme = new ContextThemeWrapper(InstallPrivilegedDialogActivity.this, FDroidApp.getCurThemeResId()); AlertDialog.Builder alertBuilder = new AlertDialog.Builder(theme) @@ -233,8 +247,8 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { .setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - InstallIntoSystemDialogActivity.this.setResult(Activity.RESULT_CANCELED); - InstallIntoSystemDialogActivity.this.finish(); + InstallPrivilegedDialogActivity.this.setResult(Activity.RESULT_CANCELED); + InstallPrivilegedDialogActivity.this.finish(); } }); alertBuilder.create().show(); @@ -254,7 +268,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { super.onPreExecute(); // hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay - ContextThemeWrapper theme = new ContextThemeWrapper(InstallIntoSystemDialogActivity.this, + ContextThemeWrapper theme = new ContextThemeWrapper(InstallPrivilegedDialogActivity.this, FDroidApp.getCurThemeResId()); mProgressDialog = new ProgressDialog(theme); @@ -266,7 +280,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { @Override protected Void doInBackground(Void... voids) { - InstallIntoSystem.create(getApplicationContext()).runInstall(); + InstallPrivileged.create(getApplicationContext()).runInstall(apkFile); return null; } }; @@ -278,10 +292,10 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { // hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId()); - final boolean success = Installer.hasSystemPermissions(this, this.getPackageManager()); + final boolean success = PrivilegedInstaller.isAvailable(this); // enable system installer on installation success - Preferences.get().setSystemInstallerEnabled(success); + Preferences.get().setPrivilegedInstallerEnabled(success); AlertDialog.Builder builder = new AlertDialog.Builder(theme) .setTitle(success ? R.string.system_install_post_success : R.string.system_install_post_fail) @@ -289,9 +303,9 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { - InstallIntoSystemDialogActivity.this.setResult(success ? Activity.RESULT_OK : Activity.RESULT_CANCELED); - InstallIntoSystemDialogActivity.this.finish(); - startActivity(new Intent(InstallIntoSystemDialogActivity.this, FDroid.class)); + InstallPrivilegedDialogActivity.this.setResult(success ? Activity.RESULT_OK : Activity.RESULT_CANCELED); + InstallPrivilegedDialogActivity.this.finish(); + startActivity(new Intent(InstallPrivilegedDialogActivity.this, FDroid.class)); } }) .setCancelable(false); @@ -302,9 +316,9 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { // hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId()); - final boolean systemApp = Installer.hasSystemPermissions(this, this.getPackageManager()); + final boolean isAvailable = PrivilegedInstaller.isAvailable(this); - if (systemApp) { + if (isAvailable) { AlertDialog.Builder builder = new AlertDialog.Builder(theme) .setTitle(R.string.system_uninstall) .setMessage(R.string.system_uninstall_message) @@ -317,8 +331,8 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - InstallIntoSystemDialogActivity.this.setResult(Activity.RESULT_CANCELED); - InstallIntoSystemDialogActivity.this.finish(); + InstallPrivilegedDialogActivity.this.setResult(Activity.RESULT_CANCELED); + InstallPrivilegedDialogActivity.this.finish(); } }); builder.create().show(); @@ -329,8 +343,8 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - InstallIntoSystemDialogActivity.this.setResult(Activity.RESULT_CANCELED); - InstallIntoSystemDialogActivity.this.finish(); + InstallPrivilegedDialogActivity.this.setResult(Activity.RESULT_CANCELED); + InstallPrivilegedDialogActivity.this.finish(); } }); builder.create().show(); @@ -345,7 +359,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { super.onPreExecute(); // hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay - ContextThemeWrapper theme = new ContextThemeWrapper(InstallIntoSystemDialogActivity.this, + ContextThemeWrapper theme = new ContextThemeWrapper(InstallPrivilegedDialogActivity.this, FDroidApp.getCurThemeResId()); mProgressDialog = new ProgressDialog(theme); @@ -357,7 +371,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { @Override protected Void doInBackground(Void... voids) { - InstallIntoSystem.create(getApplicationContext()).runUninstall(); + InstallPrivileged.create(getApplicationContext()).runUninstall(); return null; } diff --git a/F-Droid/src/org/fdroid/fdroid/installer/AppDiff.java b/F-Droid/src/org/fdroid/fdroid/privileged/views/AppDiff.java similarity index 94% rename from F-Droid/src/org/fdroid/fdroid/installer/AppDiff.java rename to F-Droid/src/org/fdroid/fdroid/privileged/views/AppDiff.java index 2391cb096..95b94cc28 100644 --- a/F-Droid/src/org/fdroid/fdroid/installer/AppDiff.java +++ b/F-Droid/src/org/fdroid/fdroid/privileged/views/AppDiff.java @@ -16,7 +16,7 @@ ** limitations under the License. */ -package org.fdroid.fdroid.installer; +package org.fdroid.fdroid.privileged.views; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; @@ -25,10 +25,10 @@ import android.net.Uri; public class AppDiff { - final PackageManager mPm; - final PackageInfo mPkgInfo; + public final PackageManager mPm; + public final PackageInfo mPkgInfo; - ApplicationInfo mInstalledAppInfo = null; + public ApplicationInfo mInstalledAppInfo = null; public AppDiff(PackageManager mPm, Uri mPackageURI) { this.mPm = mPm; diff --git a/F-Droid/src/org/fdroid/fdroid/installer/AppSecurityPermissions.java b/F-Droid/src/org/fdroid/fdroid/privileged/views/AppSecurityPermissions.java similarity index 99% rename from F-Droid/src/org/fdroid/fdroid/installer/AppSecurityPermissions.java rename to F-Droid/src/org/fdroid/fdroid/privileged/views/AppSecurityPermissions.java index e65ef76cf..c6754b71c 100644 --- a/F-Droid/src/org/fdroid/fdroid/installer/AppSecurityPermissions.java +++ b/F-Droid/src/org/fdroid/fdroid/privileged/views/AppSecurityPermissions.java @@ -15,7 +15,7 @@ ** See the License for the specific language governing permissions and ** limitations under the License. */ -package org.fdroid.fdroid.installer; +package org.fdroid.fdroid.privileged.views; import android.annotation.TargetApi; import android.content.Context; diff --git a/F-Droid/src/org/fdroid/fdroid/installer/CaffeinatedScrollView.java b/F-Droid/src/org/fdroid/fdroid/privileged/views/CaffeinatedScrollView.java similarity index 98% rename from F-Droid/src/org/fdroid/fdroid/installer/CaffeinatedScrollView.java rename to F-Droid/src/org/fdroid/fdroid/privileged/views/CaffeinatedScrollView.java index 37b96b419..f43dbc053 100644 --- a/F-Droid/src/org/fdroid/fdroid/installer/CaffeinatedScrollView.java +++ b/F-Droid/src/org/fdroid/fdroid/privileged/views/CaffeinatedScrollView.java @@ -15,7 +15,7 @@ ** limitations under the License. */ -package org.fdroid.fdroid.installer; +package org.fdroid.fdroid.privileged.views; import android.content.Context; import android.graphics.Canvas; diff --git a/F-Droid/src/org/fdroid/fdroid/installer/InstallConfirmActivity.java b/F-Droid/src/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java similarity index 99% rename from F-Droid/src/org/fdroid/fdroid/installer/InstallConfirmActivity.java rename to F-Droid/src/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java index aeaf16e8b..dae78e90f 100644 --- a/F-Droid/src/org/fdroid/fdroid/installer/InstallConfirmActivity.java +++ b/F-Droid/src/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java @@ -16,7 +16,7 @@ ** limitations under the License. */ -package org.fdroid.fdroid.installer; +package org.fdroid.fdroid.privileged.views; import android.app.Activity; import android.content.Context; diff --git a/F-Droid/src/org/fdroid/fdroid/installer/TabsAdapter.java b/F-Droid/src/org/fdroid/fdroid/privileged/views/TabsAdapter.java similarity index 99% rename from F-Droid/src/org/fdroid/fdroid/installer/TabsAdapter.java rename to F-Droid/src/org/fdroid/fdroid/privileged/views/TabsAdapter.java index eef50eb1b..02372fd84 100644 --- a/F-Droid/src/org/fdroid/fdroid/installer/TabsAdapter.java +++ b/F-Droid/src/org/fdroid/fdroid/privileged/views/TabsAdapter.java @@ -14,7 +14,7 @@ ** See the License for the specific language governing permissions and ** limitations under the License. */ -package org.fdroid.fdroid.installer; +package org.fdroid.fdroid.privileged.views; import android.app.Activity; import android.content.Context; diff --git a/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java b/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java index 4b111c0d4..00226f399 100644 --- a/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java +++ b/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java @@ -14,13 +14,14 @@ import android.support.v7.app.AlertDialog; import android.text.Html; import android.text.TextUtils; +import org.fdroid.fdroid.AppDetails; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.PreferencesActivity; import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; -import org.fdroid.fdroid.installer.InstallIntoSystemDialogActivity; -import org.fdroid.fdroid.installer.Installer; +import org.fdroid.fdroid.installer.PrivilegedInstaller; +import org.fdroid.fdroid.privileged.install.InstallPrivilegedDialogActivity; import java.util.Locale; @@ -41,7 +42,7 @@ public class PreferencesFragment extends PreferenceFragment Preferences.PREF_LANGUAGE, Preferences.PREF_CACHE_APK, Preferences.PREF_EXPERT, - Preferences.PREF_SYSTEM_INSTALLER, + Preferences.PREF_PRIVILEGED_INSTALLER, Preferences.PREF_ENABLE_PROXY, Preferences.PREF_PROXY_HOST, Preferences.PREF_PROXY_PORT, @@ -149,7 +150,7 @@ public class PreferencesFragment extends PreferenceFragment checkSummary(key, R.string.expert_on); break; - case Preferences.PREF_SYSTEM_INSTALLER: + case Preferences.PREF_PRIVILEGED_INSTALLER: checkSummary(key, R.string.system_installer_on); break; @@ -182,8 +183,8 @@ public class PreferencesFragment extends PreferenceFragment /** * Initializes SystemInstaller preference, which can only be enabled when F-Droid is installed as a system-app */ - protected void initSystemInstallerPreference() { - CheckBoxPreference pref = (CheckBoxPreference) findPreference(Preferences.PREF_SYSTEM_INSTALLER); + protected void initPrivilegedInstallerPreference() { + CheckBoxPreference pref = (CheckBoxPreference) findPreference(Preferences.PREF_PRIVILEGED_INSTALLER); // we are handling persistence ourself! pref.setPersistent(false); @@ -195,16 +196,16 @@ public class PreferencesFragment extends PreferenceFragment final CheckBoxPreference pref = (CheckBoxPreference) preference; if (pref.isChecked()) { - if (Installer.hasSystemPermissions(getActivity(), getActivity().getPackageManager())) { + if (PrivilegedInstaller.isAvailable(getActivity())) { // system-permission are granted, i.e. F-Droid is a system-app SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); - editor.putBoolean(Preferences.PREF_SYSTEM_INSTALLER, true); + editor.putBoolean(Preferences.PREF_PRIVILEGED_INSTALLER, true); editor.commit(); pref.setChecked(true); } else { // system-permission not available SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); - editor.putBoolean(Preferences.PREF_SYSTEM_INSTALLER, false); + editor.putBoolean(Preferences.PREF_PRIVILEGED_INSTALLER, false); editor.commit(); pref.setChecked(false); @@ -221,9 +222,11 @@ public class PreferencesFragment extends PreferenceFragment alertBuilder.setPositiveButton(R.string.system_permission_install_via_root, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - Intent installIntent = new Intent(getActivity(), InstallIntoSystemDialogActivity.class); - installIntent.setAction(InstallIntoSystemDialogActivity.ACTION_INSTALL); - startActivity(installIntent); + // Open details of F-Droid Privileged + Intent intent = new Intent(getActivity(), AppDetails.class); + intent.putExtra(AppDetails.EXTRA_APPID, + PrivilegedInstaller.PRIVILEGED_PACKAGE_NAME); + startActivity(intent); } }); alertBuilder.setNegativeButton(R.string.cancel, null); @@ -231,7 +234,7 @@ public class PreferencesFragment extends PreferenceFragment } } else { SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); - editor.putBoolean(Preferences.PREF_SYSTEM_INSTALLER, false); + editor.putBoolean(Preferences.PREF_PRIVILEGED_INSTALLER, false); editor.commit(); pref.setChecked(false); } @@ -241,16 +244,16 @@ public class PreferencesFragment extends PreferenceFragment }); } - protected void initUninstallSystemAppPreference() { - Preference pref = findPreference(Preferences.PREF_UNINSTALL_SYSTEM_APP); + protected void initUninstallPrivilegedAppPreference() { + Preference pref = findPreference(Preferences.PREF_UNINSTALL_PRIVILEGED_APP); pref.setPersistent(false); pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { - Intent uninstallIntent = new Intent(getActivity(), InstallIntoSystemDialogActivity.class); - uninstallIntent.setAction(InstallIntoSystemDialogActivity.ACTION_UNINSTALL); + Intent uninstallIntent = new Intent(getActivity(), InstallPrivilegedDialogActivity.class); + uninstallIntent.setAction(InstallPrivilegedDialogActivity.ACTION_UNINSTALL); startActivity(uninstallIntent); return true; @@ -280,8 +283,8 @@ public class PreferencesFragment extends PreferenceFragment updateSummary(key, false); } - initSystemInstallerPreference(); - initUninstallSystemAppPreference(); + initPrivilegedInstallerPreference(); + initUninstallPrivilegedAppPreference(); } @Override diff --git a/media/fdroid-logo-2015/fdroid-logo-privileged.svg b/media/fdroid-logo-2015/fdroid-logo-privileged.svg new file mode 100644 index 000000000..d8cf2fc8f --- /dev/null +++ b/media/fdroid-logo-2015/fdroid-logo-privileged.svg @@ -0,0 +1,359 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/media/fdroid-logo-2015/puzzle.svg b/media/fdroid-logo-2015/puzzle.svg new file mode 100644 index 000000000..1ed0d45f4 --- /dev/null +++ b/media/fdroid-logo-2015/puzzle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/privileged-api-lib/build.gradle b/privileged-api-lib/build.gradle new file mode 100644 index 000000000..392379301 --- /dev/null +++ b/privileged-api-lib/build.gradle @@ -0,0 +1,18 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 22 + buildToolsVersion '23.0.0' + + defaultConfig { + minSdkVersion 8 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + } + + // Do not abort build if lint finds errors + lintOptions { + abortOnError false + } +} \ No newline at end of file diff --git a/privileged-api-lib/src/main/AndroidManifest.xml b/privileged-api-lib/src/main/AndroidManifest.xml new file mode 100644 index 000000000..d1978ceea --- /dev/null +++ b/privileged-api-lib/src/main/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/privileged-api-lib/src/main/aidl/org/fdroid/fdroid/privileged/IPrivilegedCallback.aidl b/privileged-api-lib/src/main/aidl/org/fdroid/fdroid/privileged/IPrivilegedCallback.aidl new file mode 100644 index 000000000..fc476fafe --- /dev/null +++ b/privileged-api-lib/src/main/aidl/org/fdroid/fdroid/privileged/IPrivilegedCallback.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2015 Dominik Schürmann + * + * 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; + +interface IPrivilegedCallback { + + void handleResult(in String packageName, in int returnCode); + +} \ No newline at end of file diff --git a/privileged-api-lib/src/main/aidl/org/fdroid/fdroid/privileged/IPrivilegedService.aidl b/privileged-api-lib/src/main/aidl/org/fdroid/fdroid/privileged/IPrivilegedService.aidl new file mode 100644 index 000000000..5c1724b1d --- /dev/null +++ b/privileged-api-lib/src/main/aidl/org/fdroid/fdroid/privileged/IPrivilegedService.aidl @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015 Dominik Schürmann + * + * 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; + +import org.fdroid.fdroid.privileged.IPrivilegedCallback; + +interface IPrivilegedService { + + boolean hasPrivilegedPermissions(); + + /** + * - Docs based on PackageManager.installPackage() + * - Asynchronous (oneway) IPC calls! + * + * Install a package. Since this may take a little while, the result will + * be posted back to the given callback. An installation will fail if the + * package named in the package file's manifest is already installed, or if there's no space + * available on the device. + * + * @param packageURI The location of the package file to install. This can be a 'file:' or a + * 'content:' URI. + * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK}, + * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}. + * @param installerPackageName Optional package name of the application that is performing the + * installation. This identifies which market the package came from. + * @param callback An callback to get notified when the package installation is + * complete. + */ + oneway void installPackage(in Uri packageURI, in int flags, in String installerPackageName, + in IPrivilegedCallback callback); + + + /** + * - Docs based on PackageManager.deletePackage() + * - Asynchronous (oneway) IPC calls! + * + * Attempts to delete a package. Since this may take a little while, the result will + * be posted back to the given observer. A deletion will fail if the + * named package cannot be found, or if the named package is a "system package". + * + * @param packageName The name of the package to delete + * @param flags - possible values: {@link #DELETE_KEEP_DATA}, + * {@link #DELETE_ALL_USERS}. + * @param callback An callback to get notified when the package deletion is + * complete. + */ + oneway void deletePackage(in String packageName, in int flags, in IPrivilegedCallback callback); + +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index b037990b4..2ef4692ba 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,6 @@ include ':F-Droid' +include ':F-Droid-Privileged' +include ':privileged-api-lib' if (hasProperty('sourceDeps')) { include ':extern:support-v4-preferencefragment' include ':extern:nanohttpd:core'