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..0ff807fdd
--- /dev/null
+++ b/F-Droid-Privileged/build.gradle
@@ -0,0 +1,36 @@
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.3.1'
+ }
+}
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 22
+ buildToolsVersion '23.0.0'
+
+ defaultConfig {
+ minSdkVersion 8
+ targetSdkVersion 22
+ versionCode 1
+ versionName "1.0"
+ }
+
+ 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..127de4e1c
--- /dev/null
+++ b/F-Droid-Privileged/src/main/AndroidManifest.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/F-Droid-Privileged/src/main/aidl/org/fdroid/fdroid/privileged/IPrivilegedCallback.aidl b/F-Droid-Privileged/src/main/aidl/org/fdroid/fdroid/privileged/IPrivilegedCallback.aidl
new file mode 100644
index 000000000..fc476fafe
--- /dev/null
+++ b/F-Droid-Privileged/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/F-Droid-Privileged/src/main/aidl/org/fdroid/fdroid/privileged/IPrivilegedService.aidl b/F-Droid-Privileged/src/main/aidl/org/fdroid/fdroid/privileged/IPrivilegedService.aidl
new file mode 100644
index 000000000..b5b74ff4b
--- /dev/null
+++ b/F-Droid-Privileged/src/main/aidl/org/fdroid/fdroid/privileged/IPrivilegedService.aidl
@@ -0,0 +1,65 @@
+/*
+ * 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;
+
+/**
+ * Asynchronous (oneway) IPC calls!
+ */
+oneway interface IPrivilegedService {
+
+ /**
+ * Docs based on PackageManager.installPackage()
+ *
+ * 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.
+ */
+ void installPackage(in Uri packageURI, in int flags, in String installerPackageName,
+ in IPrivilegedCallback callback);
+
+
+ /**
+ * Docs based on PackageManager.deletePackage()
+ *
+ * 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.
+ */
+ void deletePackage(in String packageName, in int flags, in IPrivilegedCallback callback);
+
+}
\ No newline at end of file
diff --git a/F-Droid-Privileged/src/main/java/android/content/pm/IPackageDeleteObserver.java b/F-Droid-Privileged/src/main/java/android/content/pm/IPackageDeleteObserver.java
new file mode 100644
index 000000000..be0d4de81
--- /dev/null
+++ b/F-Droid-Privileged/src/main/java/android/content/pm/IPackageDeleteObserver.java
@@ -0,0 +1,54 @@
+/*
+ * 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 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 IInterface {
+
+ abstract class Stub extends Binder implements android.content.pm.IPackageDeleteObserver {
+
+ public Stub() {
+ throw new RuntimeException("Stub!");
+ }
+
+ public static IPackageDeleteObserver asInterface(IBinder obj) {
+ throw new RuntimeException("Stub!");
+ }
+
+ public IBinder asBinder() {
+ throw new RuntimeException("Stub!");
+ }
+
+ 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 RemoteException;
+}
diff --git a/F-Droid-Privileged/src/main/java/android/content/pm/IPackageInstallObserver.java b/F-Droid-Privileged/src/main/java/android/content/pm/IPackageInstallObserver.java
new file mode 100644
index 000000000..ae5b3ab12
--- /dev/null
+++ b/F-Droid-Privileged/src/main/java/android/content/pm/IPackageInstallObserver.java
@@ -0,0 +1,54 @@
+/*
+ * 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 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 IInterface {
+
+ abstract class Stub extends Binder implements android.content.pm.IPackageInstallObserver {
+
+ public Stub() {
+ throw new RuntimeException("Stub!");
+ }
+
+ public static android.content.pm.IPackageInstallObserver asInterface(IBinder obj) {
+ throw new RuntimeException("Stub!");
+ }
+
+ public IBinder asBinder() {
+ throw new RuntimeException("Stub!");
+ }
+
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ throw new RuntimeException("Stub!");
+ }
+ }
+
+ 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..d717d4b8d
--- /dev/null
+++ b/F-Droid-Privileged/src/main/java/org/fdroid/fdroid/privileged/PrivilegedService.java
@@ -0,0 +1,145 @@
+/*
+ * 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.app.Service;
+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 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 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/drawable-hdpi/ic_launcher.png b/F-Droid-Privileged/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..88138a47a
Binary files /dev/null and b/F-Droid-Privileged/src/main/res/drawable-hdpi/ic_launcher.png differ
diff --git a/F-Droid-Privileged/src/main/res/drawable-ldpi/ic_launcher.png b/F-Droid-Privileged/src/main/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 000000000..f1e8bc847
Binary files /dev/null and b/F-Droid-Privileged/src/main/res/drawable-ldpi/ic_launcher.png differ
diff --git a/F-Droid-Privileged/src/main/res/drawable-mdpi/ic_launcher.png b/F-Droid-Privileged/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..18ef68deb
Binary files /dev/null and b/F-Droid-Privileged/src/main/res/drawable-mdpi/ic_launcher.png differ
diff --git a/F-Droid-Privileged/src/main/res/drawable-xhdpi/ic_launcher.png b/F-Droid-Privileged/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..98cb52a3e
Binary files /dev/null and b/F-Droid-Privileged/src/main/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/F-Droid-Privileged/src/main/res/drawable-xxhdpi/ic_launcher.png b/F-Droid-Privileged/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..9d29cc939
Binary files /dev/null and b/F-Droid-Privileged/src/main/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/F-Droid-Privileged/src/main/res/drawable-xxxhdpi/ic_launcher.png b/F-Droid-Privileged/src/main/res/drawable-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..c012986f5
Binary files /dev/null and b/F-Droid-Privileged/src/main/res/drawable-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..e375b6e4c
--- /dev/null
+++ b/F-Droid-Privileged/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+ F-Droid Privileged
+
+