Merge branch 'enhanced-priv-install' of https://gitlab.com/dschuermann/fdroidclient
33
F-Droid-Privileged/.gitignore
vendored
Normal file
@ -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/*
|
31
F-Droid-Privileged/build.gradle
Normal file
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
41
F-Droid-Privileged/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="org.fdroid.fdroid.privileged">
|
||||||
|
|
||||||
|
<!-- These permissions are only granted when this apk is installed as a privileged app! -->
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.INSTALL_PACKAGES"
|
||||||
|
tools:ignore="ProtectedPermissions" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.DELETE_PACKAGES"
|
||||||
|
tools:ignore="ProtectedPermissions" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Only apps signed with the same key can use this permission!
|
||||||
|
The permission is automatically granted independent of the install order
|
||||||
|
Never presented to the user due to the protectionLevel.
|
||||||
|
-->
|
||||||
|
<permission
|
||||||
|
android:name="org.fdroid.fdroid.privileged.USE_SERVICE"
|
||||||
|
android:protectionLevel="signature" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="false"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name">
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".PrivilegedService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true"
|
||||||
|
android:permission="org.fdroid.fdroid.privileged.USE_SERVICE"
|
||||||
|
android:process=":fdroid_privileged">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="org.fdroid.fdroid.privileged.IPrivilegedService" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@ -19,31 +19,36 @@
|
|||||||
|
|
||||||
package android.content.pm;
|
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!
|
* 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() {
|
public Stub() {
|
||||||
throw new RuntimeException("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!");
|
throw new RuntimeException("Stub!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public android.os.IBinder asBinder() {
|
public IBinder asBinder() {
|
||||||
throw new RuntimeException("Stub!");
|
throw new RuntimeException("Stub!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
|
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
|
||||||
int flags) throws android.os.RemoteException {
|
throws RemoteException {
|
||||||
throw new RuntimeException("Stub!");
|
throw new RuntimeException("Stub!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void packageDeleted(java.lang.String packageName, int returnCode)
|
void packageDeleted(java.lang.String packageName, int returnCode) throws RemoteException;
|
||||||
throws android.os.RemoteException;
|
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@ -19,31 +19,36 @@
|
|||||||
|
|
||||||
package android.content.pm;
|
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!
|
* 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() {
|
public Stub() {
|
||||||
throw new RuntimeException("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!");
|
throw new RuntimeException("Stub!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public android.os.IBinder asBinder() {
|
public IBinder asBinder() {
|
||||||
throw new RuntimeException("Stub!");
|
throw new RuntimeException("Stub!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
|
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
|
||||||
int flags) throws android.os.RemoteException {
|
throws RemoteException {
|
||||||
throw new RuntimeException("Stub!");
|
throw new RuntimeException("Stub!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void packageInstalled(java.lang.String packageName, int returnCode)
|
void packageInstalled(String packageName, int returnCode) throws RemoteException;
|
||||||
throws android.os.RemoteException;
|
|
||||||
}
|
}
|
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
BIN
F-Droid-Privileged/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
F-Droid-Privileged/src/main/res/mipmap-ldpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
F-Droid-Privileged/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
F-Droid-Privileged/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
F-Droid-Privileged/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 9.2 KiB |
BIN
F-Droid-Privileged/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 13 KiB |
6
F-Droid-Privileged/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string name="app_name">F-Droid Privileged Extension</string>
|
||||||
|
|
||||||
|
</resources>
|
@ -51,11 +51,7 @@
|
|||||||
android:maxSdkVersion="18" />
|
android:maxSdkVersion="18" />
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
|
|
||||||
<!-- These permissions are only granted when F-Droid is installed as a system-app! -->
|
<uses-permission android:name="org.fdroid.fdroid.privileged.USE_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.INSTALL_PACKAGES"
|
|
||||||
tools:ignore="ProtectedPermissions"/>
|
|
||||||
<uses-permission android:name="android.permission.DELETE_PACKAGES"
|
|
||||||
tools:ignore="ProtectedPermissions"/>
|
|
||||||
|
|
||||||
<!-- Indicate that F-Droid may request root access (introduced by Koush's Superuser app)
|
<!-- Indicate that F-Droid may request root access (introduced by Koush's Superuser app)
|
||||||
This permission is deprecated, but necessary for some old superuser
|
This permission is deprecated, but necessary for some old superuser
|
||||||
@ -302,7 +298,7 @@
|
|||||||
android:value=".SearchResults" />
|
android:value=".SearchResults" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".installer.InstallConfirmActivity"
|
android:name=".privileged.views.InstallConfirmActivity"
|
||||||
android:label="@string/menu_install"
|
android:label="@string/menu_install"
|
||||||
android:parentActivityName=".FDroid">
|
android:parentActivityName=".FDroid">
|
||||||
<meta-data
|
<meta-data
|
||||||
@ -390,10 +386,10 @@
|
|||||||
</activity>
|
</activity>
|
||||||
<!-- Note: Theme.NoDisplay, this activity shows dialogs only -->
|
<!-- Note: Theme.NoDisplay, this activity shows dialogs only -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".installer.InstallIntoSystemDialogActivity"
|
android:name=".privileged.install.InstallPrivilegedDialogActivity"
|
||||||
android:theme="@android:style/Theme.NoDisplay" />
|
android:theme="@android:style/Theme.NoDisplay" />
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".installer.InstallIntoSystemBootReceiver" >
|
android:name=".privileged.install.InstallPrivilegedBootReceiver" >
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
@ -12,6 +12,7 @@ if (!hasProperty('sourceDeps')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
compile project(':privileged-api-lib')
|
||||||
|
|
||||||
compile 'com.android.support:support-v4:22.2.1'
|
compile 'com.android.support:support-v4:22.2.1'
|
||||||
compile 'com.android.support:appcompat-v7:22.1.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:appcompat-v7:22.1.1'
|
||||||
compile 'com.android.support:support-annotations:22.2.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 'org.thoughtcrime.ssl.pinning:AndroidPinning:1.0.0'
|
||||||
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.4'
|
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.4'
|
||||||
compile 'com.google.zxing:core:3.2.1'
|
compile 'com.google.zxing:core:3.2.1'
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
Defines the layout of a single permission item.
|
Defines the layout of a single permission item.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<view class="org.fdroid.fdroid.installer.AppSecurityPermissions$PermissionItemView"
|
<view class="org.fdroid.fdroid.privileged.views.AppSecurityPermissions$PermissionItemView"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
Defines the layout of a single permission item.
|
Defines the layout of a single permission item.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<view class="org.fdroid.fdroid.installer.AppSecurityPermissions$PermissionItemView"
|
<view class="org.fdroid.fdroid.privileged.views.AppSecurityPermissions$PermissionItemView"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
Defines the layout of a single permission item that costs money.
|
Defines the layout of a single permission item that costs money.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<view class="org.fdroid.fdroid.installer.AppSecurityPermissions$PermissionItemView"
|
<view class="org.fdroid.fdroid.privileged.views.AppSecurityPermissions$PermissionItemView"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<!--
|
<!--
|
||||||
This is the structure for the list of all permissions.
|
This is the structure for the list of all permissions.
|
||||||
-->
|
-->
|
||||||
<org.fdroid.fdroid.installer.CaffeinatedScrollView
|
<org.fdroid.fdroid.privileged.views.CaffeinatedScrollView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/scrollview"
|
android:id="@+id/scrollview"
|
||||||
android:fillViewport="true">
|
android:fillViewport="true">
|
||||||
@ -50,4 +50,4 @@ This is the structure for the list of all permissions.
|
|||||||
android:text="@string/devicePerms" />
|
android:text="@string/devicePerms" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</org.fdroid.fdroid.installer.CaffeinatedScrollView>
|
</org.fdroid.fdroid.privileged.views.CaffeinatedScrollView>
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<string name="system_installer">Enable privileged F-Droid</string>
|
<string name="system_installer">Enable privileged F-Droid</string>
|
||||||
<string name="system_installer_on">Use privileged permissions to install, update, and remove packages</string>
|
<string name="system_installer_on">Use privileged permissions to install, update, and remove packages</string>
|
||||||
<string name="uninstall_system">Uninstall privileged F-Droid</string>
|
<string name="uninstall_system">Uninstall privileged F-Droid</string>
|
||||||
<string name="uninstall_system_summary">Uninstall F-Droid when installed as a privileged app</string>
|
<string name="uninstall_system_summary">Uninstalls the additional privileged F-Droid app</string>
|
||||||
<string name="local_repo_name">Name of your Local Repo</string>
|
<string name="local_repo_name">Name of your Local Repo</string>
|
||||||
<string name="local_repo_name_summary">The advertised title of your local repo: %s</string>
|
<string name="local_repo_name_summary">The advertised title of your local repo: %s</string>
|
||||||
<string name="local_repo_https">Use Private Connection</string>
|
<string name="local_repo_https">Use Private Connection</string>
|
||||||
@ -296,23 +296,23 @@
|
|||||||
<string name="install_error_cannot_parse">An error occurred while parsing the package</string>
|
<string name="install_error_cannot_parse">An error occurred while parsing the package</string>
|
||||||
<string name="uninstall_error_title">Uninstall error</string>
|
<string name="uninstall_error_title">Uninstall error</string>
|
||||||
<string name="uninstall_error_unknown">Failed to uninstall due to an unknown error</string>
|
<string name="uninstall_error_unknown">Failed to uninstall due to an unknown error</string>
|
||||||
<string name="system_permission_denied_title">System permissions denied</string>
|
<string name="system_permission_denied_title">Privileged F-Droid is not available</string>
|
||||||
<string name="system_permission_denied_body">This option is only available when F-Droid is installed as a privileged app.</string>
|
<string name="system_permission_denied_body">This option is only available when privileged F-Droid is installed.</string>
|
||||||
<string name="system_permission_install_via_root">Install as a privileged app</string>
|
<string name="system_permission_install_via_root">Install privileged F-Droid</string>
|
||||||
<string name="system_install_first_time_notification">Install privileged F-Droid?</string>
|
<string name="system_install_first_time_notification">Install privileged F-Droid?</string>
|
||||||
<string name="system_install_first_time_notification_message_short">Touch for more information.</string>
|
<string name="system_install_first_time_notification_message_short">Touch for more information.</string>
|
||||||
<string name="system_install_first_time_notification_message">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.</string>
|
<string name="system_install_first_time_notification_message">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.</string>
|
||||||
<string name="system_install_post_success">Successful installation as a privileged app</string>
|
<string name="system_install_post_success">Successfully installed privileged F-Droid</string>
|
||||||
<string name="system_install_post_fail">Installation as a privileged app failed</string>
|
<string name="system_install_post_fail">Installation of privileged F-Droid failed</string>
|
||||||
<string name="system_install_post_success_message">F-Droid has been successfully installed as a privileged app. This allows F-Droid to install, upgrade and uninstall apps on its own.</string>
|
<string name="system_install_post_success_message">Privileged F-Droid has been successfully installed. This allows F-Droid to install, upgrade and uninstall apps on its own.</string>
|
||||||
<string name="system_install_post_fail_message">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.</string>
|
<string name="system_install_post_fail_message">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.</string>
|
||||||
<string name="system_install_installing">installing…</string>
|
<string name="system_install_installing">installing…</string>
|
||||||
<string name="system_install_uninstalling">uninstalling…</string>
|
<string name="system_install_uninstalling">uninstalling…</string>
|
||||||
<string name="system_install_question">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.</string>
|
<string name="system_install_question">Do you want to install privileged F-Droid?\nThis takes up to 10 seconds.</string>
|
||||||
<string name="system_install_question_lollipop">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.</string>
|
<string name="system_install_question_lollipop">Do you want to install privileged F-Droid?\nThis takes up to 10 seconds and the device will be <b>rebooted</b> afterwards.</string>
|
||||||
<string name="system_install_first_time_message">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.</string>
|
<string name="system_install_first_time_message">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.</string>
|
||||||
<string name="system_uninstall">Do you want to uninstall F-Droid?</string>
|
<string name="system_uninstall">Do you want to uninstall privileged F-Droid?</string>
|
||||||
<string name="system_uninstall_message">This will uninstall F-Droid completely.</string>
|
<string name="system_uninstall_message">This will uninstall privileged F-Droid.</string>
|
||||||
<string name="system_uninstall_button">Uninstall</string>
|
<string name="system_uninstall_button">Uninstall</string>
|
||||||
|
|
||||||
<string name="app_description">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.</string>
|
<string name="app_description">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.</string>
|
||||||
|
@ -75,11 +75,11 @@
|
|||||||
android:key="expert" />
|
android:key="expert" />
|
||||||
<CheckBoxPreference android:title="@string/system_installer"
|
<CheckBoxPreference android:title="@string/system_installer"
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="systemInstaller"
|
android:key="privilegedInstaller"
|
||||||
android:dependency="expert" />
|
android:dependency="expert" />
|
||||||
<Preference android:title="@string/uninstall_system"
|
<Preference android:title="@string/uninstall_system"
|
||||||
android:summary="@string/uninstall_system_summary"
|
android:summary="@string/uninstall_system_summary"
|
||||||
android:key="uninstallSystemApp"
|
android:key="uninstallPrivilegedApp"
|
||||||
android:dependency="expert" />
|
android:dependency="expert" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
@ -892,7 +892,7 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
|
|||||||
|
|
||||||
private void installApk(File file) {
|
private void installApk(File file) {
|
||||||
try {
|
try {
|
||||||
installer.installPackage(file);
|
installer.installPackage(file, app.id);
|
||||||
} catch (AndroidNotCompatibleException e) {
|
} catch (AndroidNotCompatibleException e) {
|
||||||
Log.e(TAG, "Android not compatible with this Installer!", e);
|
Log.e(TAG, "Android not compatible with this Installer!", e);
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ import android.widget.Toast;
|
|||||||
import org.fdroid.fdroid.compat.TabManager;
|
import org.fdroid.fdroid.compat.TabManager;
|
||||||
import org.fdroid.fdroid.data.AppProvider;
|
import org.fdroid.fdroid.data.AppProvider;
|
||||||
import org.fdroid.fdroid.data.NewRepoConfig;
|
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.AppListFragmentPagerAdapter;
|
||||||
import org.fdroid.fdroid.views.ManageReposActivity;
|
import org.fdroid.fdroid.views.ManageReposActivity;
|
||||||
import org.fdroid.fdroid.views.swap.SwapWorkflowActivity;
|
import org.fdroid.fdroid.views.swap.SwapWorkflowActivity;
|
||||||
@ -98,7 +98,7 @@ public class FDroid extends ActionBarActivity {
|
|||||||
Uri uri = AppProvider.getContentUri();
|
Uri uri = AppProvider.getContentUri();
|
||||||
getContentResolver().registerContentObserver(uri, true, new AppObserver());
|
getContentResolver().registerContentObserver(uri, true, new AppObserver());
|
||||||
|
|
||||||
InstallIntoSystemDialogActivity.firstTime(this);
|
InstallPrivilegedDialogActivity.firstTime(this);
|
||||||
|
|
||||||
if (UpdateService.isNetworkAvailableForUpdate(this)) {
|
if (UpdateService.isNetworkAvailableForUpdate(this)) {
|
||||||
UpdateService.updateNow(this);
|
UpdateService.updateNow(this);
|
||||||
|
@ -49,8 +49,8 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi
|
|||||||
public static final String PREF_CACHE_APK = "cacheDownloaded";
|
public static final String PREF_CACHE_APK = "cacheDownloaded";
|
||||||
public static final String PREF_EXPERT = "expert";
|
public static final String PREF_EXPERT = "expert";
|
||||||
public static final String PREF_UPD_LAST = "lastUpdateCheck";
|
public static final String PREF_UPD_LAST = "lastUpdateCheck";
|
||||||
public static final String PREF_SYSTEM_INSTALLER = "systemInstaller";
|
public static final String PREF_PRIVILEGED_INSTALLER = "privilegedInstaller";
|
||||||
public static final String PREF_UNINSTALL_SYSTEM_APP = "uninstallSystemApp";
|
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";
|
||||||
@ -59,12 +59,12 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi
|
|||||||
public static final String PREF_PROXY_PORT = "proxyPort";
|
public static final String PREF_PROXY_PORT = "proxyPort";
|
||||||
public static final String PREF_SHOW_NFC_DURING_SWAP = "showNfcDuringSwap";
|
public static final String PREF_SHOW_NFC_DURING_SWAP = "showNfcDuringSwap";
|
||||||
public static final String PREF_FIRST_TIME = "firstTime";
|
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_COMPACT_LAYOUT = false;
|
||||||
private static final boolean DEFAULT_ROOTED = true;
|
private static final boolean DEFAULT_ROOTED = true;
|
||||||
private static final int DEFAULT_UPD_HISTORY = 14;
|
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_LOCAL_REPO_BONJOUR = true;
|
||||||
private static final boolean DEFAULT_CACHE_APK = false;
|
private static final boolean DEFAULT_CACHE_APK = false;
|
||||||
private static final boolean DEFAULT_LOCAL_REPO_HTTPS = 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 int DEFAULT_PROXY_PORT = 8118;
|
||||||
public static final boolean DEFAULT_SHOW_NFC_DURING_SWAP = true;
|
public static final boolean DEFAULT_SHOW_NFC_DURING_SWAP = true;
|
||||||
private static final boolean DEFAULT_FIRST_TIME = 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 compactLayout = DEFAULT_COMPACT_LAYOUT;
|
||||||
private boolean filterAppsRequiringRoot = DEFAULT_ROOTED;
|
private boolean filterAppsRequiringRoot = DEFAULT_ROOTED;
|
||||||
@ -101,12 +101,12 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi
|
|||||||
initialized.put(key, false);
|
initialized.put(key, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSystemInstallerEnabled() {
|
public boolean isPrivilegedInstallerEnabled() {
|
||||||
return preferences.getBoolean(PREF_SYSTEM_INSTALLER, DEFAULT_SYSTEM_INSTALLER);
|
return preferences.getBoolean(PREF_PRIVILEGED_INSTALLER, DEFAULT_PRIVILEGED_INSTALLER);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSystemInstallerEnabled(boolean enable) {
|
public void setPrivilegedInstallerEnabled(boolean enable) {
|
||||||
preferences.edit().putBoolean(PREF_SYSTEM_INSTALLER, enable).commit();
|
preferences.edit().putBoolean(PREF_PRIVILEGED_INSTALLER, enable).commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFirstTime() {
|
public boolean isFirstTime() {
|
||||||
@ -117,12 +117,12 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi
|
|||||||
preferences.edit().putBoolean(PREF_FIRST_TIME, firstTime).commit();
|
preferences.edit().putBoolean(PREF_FIRST_TIME, firstTime).commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPostSystemInstall() {
|
public boolean isPostPrivilegedInstall() {
|
||||||
return preferences.getBoolean(PREF_POST_SYSTEM_INSTALL, DEFAULT_POST_SYSTEM_INSTALL);
|
return preferences.getBoolean(PREF_POST_PRIVILEGED_INSTALL, DEFAULT_POST_PRIVILEGED_INSTALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPostSystemInstall(boolean postInstall) {
|
public void setPostPrivilegedInstall(boolean postInstall) {
|
||||||
preferences.edit().putBoolean(PREF_POST_SYSTEM_INSTALL, postInstall).commit();
|
preferences.edit().putBoolean(PREF_POST_PRIVILEGED_INSTALL, postInstall).commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean shouldCacheApks() {
|
public boolean shouldCacheApks() {
|
||||||
|
@ -39,10 +39,10 @@ import java.util.List;
|
|||||||
* unattended installations.
|
* unattended installations.
|
||||||
*/
|
*/
|
||||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
public class DefaultInstallerSdk14 extends Installer {
|
public class DefaultSdk14Installer extends Installer {
|
||||||
private final Activity mActivity;
|
private final Activity mActivity;
|
||||||
|
|
||||||
public DefaultInstallerSdk14(Activity activity, PackageManager pm, InstallerCallback callback)
|
public DefaultSdk14Installer(Activity activity, PackageManager pm, InstallerCallback callback)
|
||||||
throws AndroidNotCompatibleException {
|
throws AndroidNotCompatibleException {
|
||||||
super(activity, pm, callback);
|
super(activity, pm, callback);
|
||||||
this.mActivity = activity;
|
this.mActivity = activity;
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package org.fdroid.fdroid.installer;
|
package org.fdroid.fdroid.installer;
|
||||||
|
|
||||||
import android.Manifest.permission;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -28,6 +27,7 @@ import android.util.Log;
|
|||||||
|
|
||||||
import org.fdroid.fdroid.Preferences;
|
import org.fdroid.fdroid.Preferences;
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
|
import org.fdroid.fdroid.privileged.install.InstallPrivilegedDialogActivity;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -107,13 +107,13 @@ abstract public class Installer {
|
|||||||
InstallerCallback callback) {
|
InstallerCallback callback) {
|
||||||
|
|
||||||
// system permissions and pref enabled -> SystemInstaller
|
// system permissions and pref enabled -> SystemInstaller
|
||||||
boolean isSystemInstallerEnabled = Preferences.get().isSystemInstallerEnabled();
|
boolean isSystemInstallerEnabled = Preferences.get().isPrivilegedInstallerEnabled();
|
||||||
if (isSystemInstallerEnabled) {
|
if (isSystemInstallerEnabled) {
|
||||||
if (hasSystemPermissions(activity, pm)) {
|
if (PrivilegedInstaller.isAvailable(activity)) {
|
||||||
Utils.DebugLog(TAG, "system permissions -> SystemInstaller");
|
Utils.DebugLog(TAG, "system permissions -> SystemInstaller");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new SystemInstaller(activity, pm, callback);
|
return new PrivilegedInstaller(activity, pm, callback);
|
||||||
} catch (AndroidNotCompatibleException e) {
|
} catch (AndroidNotCompatibleException e) {
|
||||||
Log.e(TAG, "Android not compatible with SystemInstaller!", e);
|
Log.e(TAG, "Android not compatible with SystemInstaller!", e);
|
||||||
}
|
}
|
||||||
@ -128,7 +128,7 @@ abstract public class Installer {
|
|||||||
try {
|
try {
|
||||||
Utils.DebugLog(TAG, "try default installer for Android >= 4");
|
Utils.DebugLog(TAG, "try default installer for Android >= 4");
|
||||||
|
|
||||||
return new DefaultInstallerSdk14(activity, pm, callback);
|
return new DefaultSdk14Installer(activity, pm, callback);
|
||||||
} catch (AndroidNotCompatibleException e) {
|
} catch (AndroidNotCompatibleException e) {
|
||||||
Log.e(TAG, "Android not compatible with DefaultInstallerSdk14!", e);
|
Log.e(TAG, "Android not compatible with DefaultInstallerSdk14!", e);
|
||||||
}
|
}
|
||||||
@ -147,24 +147,30 @@ abstract public class Installer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasSystemPermissions(Context context, PackageManager pm) {
|
public void installPackage(File apkFile, String packageName) throws AndroidNotCompatibleException {
|
||||||
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 {
|
|
||||||
// check if file exists...
|
// check if file exists...
|
||||||
if (!apkFile.exists()) {
|
if (!apkFile.exists()) {
|
||||||
Log.e(TAG, "Couldn't find file " + apkFile + " to install.");
|
Log.e(TAG, "Couldn't find file " + apkFile + " to install.");
|
||||||
return;
|
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);
|
installPackageInternal(apkFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,22 +21,29 @@
|
|||||||
package org.fdroid.fdroid.installer;
|
package org.fdroid.fdroid.installer;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.IPackageDeleteObserver;
|
|
||||||
import android.content.pm.IPackageInstallObserver;
|
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.Utils;
|
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.io.File;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,78 +70,69 @@ import java.util.List;
|
|||||||
* https://android.googlesource.com/platform
|
* https://android.googlesource.com/platform
|
||||||
* /frameworks/base/+/ccbf84f44c9e6a5ed3c08673614826bb237afc54
|
* /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 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 static final int REQUEST_CONFIRM_PERMS = 0;
|
||||||
|
|
||||||
public SystemInstaller(Activity activity, PackageManager pm,
|
public PrivilegedInstaller(Activity activity, PackageManager pm,
|
||||||
InstallerCallback callback) throws AndroidNotCompatibleException {
|
InstallerCallback callback) throws AndroidNotCompatibleException {
|
||||||
super(activity, pm, callback);
|
super(activity, pm, callback);
|
||||||
this.mActivity = activity;
|
this.mActivity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
// create internal callbacks
|
public static boolean isAvailable(Context context) {
|
||||||
mInstallObserver = new PackageInstallObserver();
|
|
||||||
mDeleteObserver = new PackageDeleteObserver();
|
|
||||||
|
|
||||||
|
// check if installed
|
||||||
|
PackageManager pm = context.getPackageManager();
|
||||||
try {
|
try {
|
||||||
Class<?>[] installTypes = {
|
pm.getPackageInfo(PRIVILEGED_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
|
||||||
Uri.class, IPackageInstallObserver.class, int.class,
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
String.class
|
return false;
|
||||||
};
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// check if it has the privileged permissions granted
|
||||||
* Internal install callback from the system
|
final Object mutex = new Object();
|
||||||
*/
|
final Bundle returnBundle = new Bundle();
|
||||||
class PackageInstallObserver extends IPackageInstallObserver.Stub {
|
ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||||
public void packageInstalled(String packageName, int returnCode) throws RemoteException {
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
// TODO: propagate other return codes?
|
IPrivilegedService privService = IPrivilegedService.Stub.asInterface(service);
|
||||||
if (returnCode == INSTALL_SUCCEEDED) {
|
|
||||||
Utils.DebugLog(TAG, "Install succeeded");
|
|
||||||
|
|
||||||
mCallback.onSuccess(InstallerCallback.OPERATION_INSTALL);
|
try {
|
||||||
} else {
|
boolean hasPermissions = privService.hasPrivilegedPermissions();
|
||||||
Log.e(TAG, "Install failed with returnCode " + returnCode);
|
returnBundle.putBoolean("has_permissions", hasPermissions);
|
||||||
mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
} catch (RemoteException e) {
|
||||||
InstallerCallback.ERROR_CODE_OTHER);
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return (returnBundle.getBoolean("has_permission", false));
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -176,15 +174,47 @@ public class SystemInstaller extends Installer {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doInstallPackageInternal(Uri packageURI) throws AndroidNotCompatibleException {
|
private void doInstallPackageInternal(final Uri packageURI) throws AndroidNotCompatibleException {
|
||||||
try {
|
ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||||
mInstallMethod.invoke(mPm, packageURI, mInstallObserver,
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
INSTALL_REPLACE_EXISTING, null);
|
IPrivilegedService privService = IPrivilegedService.Stub.asInterface(service);
|
||||||
} catch (Exception e) {
|
|
||||||
throw new AndroidNotCompatibleException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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
|
@Override
|
||||||
protected void installPackageInternal(List<File> apkFiles)
|
protected void installPackageInternal(List<File> apkFiles)
|
||||||
@ -250,35 +280,70 @@ public class SystemInstaller extends Installer {
|
|||||||
|
|
||||||
private void doDeletePackageInternal(final String packageName)
|
private void doDeletePackageInternal(final String packageName)
|
||||||
throws AndroidNotCompatibleException {
|
throws AndroidNotCompatibleException {
|
||||||
try {
|
ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||||
mDeleteMethod.invoke(mPm, packageName, mDeleteObserver, 0);
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
} catch (Exception e) {
|
IPrivilegedService privService = IPrivilegedService.Stub.asInterface(service);
|
||||||
throw new AndroidNotCompatibleException(e);
|
|
||||||
}
|
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
|
@Override
|
||||||
public boolean handleOnActivityResult(int requestCode, int resultCode, Intent data) {
|
public boolean handleOnActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
case REQUEST_CONFIRM_PERMS:
|
case REQUEST_CONFIRM_PERMS:
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
final Uri packageUri = data.getData();
|
final Uri packageUri = data.getData();
|
||||||
try {
|
try {
|
||||||
doInstallPackageInternal(packageUri);
|
doInstallPackageInternal(packageUri);
|
||||||
} catch (AndroidNotCompatibleException e) {
|
} catch (AndroidNotCompatibleException e) {
|
||||||
|
mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
||||||
|
InstallerCallback.ERROR_CODE_OTHER);
|
||||||
|
}
|
||||||
|
} else if (resultCode == InstallConfirmActivity.RESULT_CANNOT_PARSE) {
|
||||||
mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
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) {
|
return true;
|
||||||
mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
default:
|
||||||
InstallerCallback.ERROR_CODE_CANNOT_PARSE);
|
return false;
|
||||||
} else { // Activity.RESULT_CANCELED
|
|
||||||
mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
|
||||||
InstallerCallback.ERROR_CODE_CANCELED);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -579,7 +644,7 @@ public class SystemInstaller extends Installer {
|
|||||||
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
|
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
|
||||||
* if the system failed to install the package because it is attempting to define a
|
* if the system failed to install the package because it is attempting to define a
|
||||||
* permission that is already defined by some existing package.
|
* permission that is already defined by some existing package.
|
||||||
*
|
* <p/>
|
||||||
* <p>The package name of the app which has already defined the permission is passed to
|
* <p>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}
|
* 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
|
* string extra; and the name of the permission being redefined is passed in the
|
@ -17,13 +17,14 @@
|
|||||||
* MA 02110-1301, USA.
|
* MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.fdroid.fdroid.installer;
|
package org.fdroid.fdroid.privileged.install;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import org.fdroid.fdroid.Preferences;
|
import org.fdroid.fdroid.Preferences;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
|
import org.fdroid.fdroid.installer.PrivilegedInstaller;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
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/
|
* 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
|
* 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;
|
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;
|
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) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
return new LollipopImpl(context);
|
return new LollipopImpl(context);
|
||||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
@ -65,10 +68,10 @@ abstract class InstallIntoSystem {
|
|||||||
|
|
||||||
final void runUninstall() {
|
final void runUninstall() {
|
||||||
final String[] commands = {
|
final String[] commands = {
|
||||||
"am force-stop org.fdroid.fdroid",
|
"am force-stop " + PrivilegedInstaller.PRIVILEGED_PACKAGE_NAME,
|
||||||
"pm clear org.fdroid.fdroid",
|
"pm clear " + PrivilegedInstaller.PRIVILEGED_PACKAGE_NAME,
|
||||||
"mount -o rw,remount /system",
|
"mount -o rw,remount /system",
|
||||||
"pm uninstall " + context.getPackageName(),
|
"pm uninstall " + PrivilegedInstaller.PRIVILEGED_PACKAGE_NAME,
|
||||||
"rm -f " + getInstallPath(),
|
"rm -f " + getInstallPath(),
|
||||||
"sleep 5",
|
"sleep 5",
|
||||||
"mount -o ro,remount /system"
|
"mount -o ro,remount /system"
|
||||||
@ -76,32 +79,32 @@ abstract class InstallIntoSystem {
|
|||||||
Shell.SU.run(commands);
|
Shell.SU.run(commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
final void runInstall() {
|
final void runInstall(String apkPath) {
|
||||||
onPreInstall();
|
onPreInstall();
|
||||||
Shell.SU.run(getInstallCommands());
|
Shell.SU.run(getInstallCommands(apkPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getInstallPath() {
|
protected String getInstallPath() {
|
||||||
return getSystemFolder() + "FDroid.apk";
|
return getSystemFolder() + APK_FILE_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getInstallCommands() {
|
private List<String> getInstallCommands(String apkPath) {
|
||||||
final List<String> commands = new ArrayList<>();
|
final List<String> commands = new ArrayList<>();
|
||||||
commands.add("mount -o rw,remount /system");
|
commands.add("mount -o rw,remount /system");
|
||||||
commands.addAll(getCopyToSystemCommands());
|
commands.addAll(getCopyToSystemCommands(apkPath));
|
||||||
commands.add("pm uninstall -k " + context.getPackageName()); // -k to retain data
|
commands.add("pm uninstall " + PrivilegedInstaller.PRIVILEGED_PACKAGE_NAME);
|
||||||
commands.add("mv " + getInstallPath() + ".tmp " + getInstallPath());
|
commands.add("mv " + getInstallPath() + ".tmp " + getInstallPath());
|
||||||
commands.add("pm install -r " + getInstallPath());
|
commands.add("pm install -r " + getInstallPath());
|
||||||
commands.add("sleep 5"); // wait until the app is really installed
|
commands.add("sleep 5"); // wait until the app is really installed
|
||||||
commands.add("mount -o ro,remount /system");
|
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());
|
commands.addAll(getPostInstallCommands());
|
||||||
return commands;
|
return commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<String> getCopyToSystemCommands() {
|
protected List<String> getCopyToSystemCommands(String apkPath) {
|
||||||
final List<String> commands = new ArrayList<>(2);
|
final List<String> commands = new ArrayList<>(2);
|
||||||
commands.add("cat " + context.getPackageCodePath() + " > " + getInstallPath() + ".tmp");
|
commands.add("cat " + apkPath + " > " + getInstallPath() + ".tmp");
|
||||||
commands.add("chmod 644 " + getInstallPath() + ".tmp");
|
commands.add("chmod 644 " + getInstallPath() + ".tmp");
|
||||||
return commands;
|
return commands;
|
||||||
}
|
}
|
||||||
@ -112,7 +115,7 @@ abstract class InstallIntoSystem {
|
|||||||
return commands;
|
return commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PreKitKatImpl extends InstallIntoSystem {
|
private static class PreKitKatImpl extends InstallPrivileged {
|
||||||
|
|
||||||
public PreKitKatImpl(Context context) {
|
public PreKitKatImpl(Context context) {
|
||||||
super(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) {
|
public KitKatToLollipopImpl(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
@ -146,7 +149,7 @@ abstract class InstallIntoSystem {
|
|||||||
* History of PackageManagerService in Lollipop:
|
* History of PackageManagerService in Lollipop:
|
||||||
* https://github.com/android/platform_frameworks_base/commits/lollipop-release/services/core/java/com/android/server/pm/PackageManagerService.java
|
* 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) {
|
public LollipopImpl(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
@ -155,7 +158,7 @@ abstract class InstallIntoSystem {
|
|||||||
@Override
|
@Override
|
||||||
protected void onPreInstall() {
|
protected void onPreInstall() {
|
||||||
// Setup preference to execute postInstall after reboot
|
// Setup preference to execute postInstall after reboot
|
||||||
Preferences.get().setPostSystemInstall(true);
|
Preferences.get().setPostPrivilegedInstall(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getWarningInfo() {
|
public String getWarningInfo() {
|
||||||
@ -167,24 +170,24 @@ abstract class InstallIntoSystem {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected String getSystemFolder() {
|
protected String getSystemFolder() {
|
||||||
return "/system/priv-app/FDroid/";
|
return "/system/priv-app/FDroidPrivileged/";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create app directory
|
* Create app directory
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected List<String> getCopyToSystemCommands() {
|
protected List<String> getCopyToSystemCommands(String apkPath) {
|
||||||
List<String> commands = new ArrayList<>(3);
|
List<String> commands = new ArrayList<>(3);
|
||||||
commands.add("mkdir -p " + getSystemFolder()); // create app directory if not existing
|
commands.add("mkdir -p " + getSystemFolder()); // create app directory if not existing
|
||||||
commands.add("chmod 755 " + getSystemFolder());
|
commands.add("chmod 755 " + getSystemFolder());
|
||||||
commands.add("cat " + context.getPackageCodePath() + " > " + getInstallPath() + ".tmp");
|
commands.add("cat " + apkPath + " > " + getInstallPath() + ".tmp");
|
||||||
commands.add("chmod 644 " + getInstallPath() + ".tmp");
|
commands.add("chmod 644 " + getInstallPath() + ".tmp");
|
||||||
return commands;
|
return commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Currently only works with reboot
|
* NOTE: Only works with reboot
|
||||||
* <p/>
|
* <p/>
|
||||||
* File observers on /system/priv-app/ have been removed because they don't work with the new
|
* File observers on /system/priv-app/ have been removed because they don't work with the new
|
||||||
* cluser-style layout. See
|
* cluser-style layout. See
|
@ -17,7 +17,7 @@
|
|||||||
* MA 02110-1301, USA.
|
* MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.fdroid.fdroid.installer;
|
package org.fdroid.fdroid.privileged.install;
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -25,16 +25,16 @@ import android.content.Intent;
|
|||||||
|
|
||||||
import org.fdroid.fdroid.Preferences;
|
import org.fdroid.fdroid.Preferences;
|
||||||
|
|
||||||
public class InstallIntoSystemBootReceiver extends BroadcastReceiver {
|
public class InstallPrivilegedBootReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
|
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
|
||||||
if (Preferences.get().isPostSystemInstall()) {
|
if (Preferences.get().isPostPrivilegedInstall()) {
|
||||||
Preferences.get().setPostSystemInstall(false);
|
Preferences.get().setPostPrivilegedInstall(false);
|
||||||
|
|
||||||
Intent postInstall = new Intent(context.getApplicationContext(), InstallIntoSystemDialogActivity.class);
|
Intent postInstall = new Intent(context.getApplicationContext(), InstallPrivilegedDialogActivity.class);
|
||||||
postInstall.setAction(InstallIntoSystemDialogActivity.ACTION_POST_INSTALL);
|
postInstall.setAction(InstallPrivilegedDialogActivity.ACTION_POST_INSTALL);
|
||||||
postInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
postInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
context.startActivity(postInstall);
|
context.startActivity(postInstall);
|
||||||
}
|
}
|
@ -17,7 +17,7 @@
|
|||||||
* MA 02110-1301, USA.
|
* MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.fdroid.fdroid.installer;
|
package org.fdroid.fdroid.privileged.install;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
@ -36,26 +36,33 @@ import android.text.Html;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.ContextThemeWrapper;
|
import android.view.ContextThemeWrapper;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.AppDetails;
|
||||||
import org.fdroid.fdroid.FDroid;
|
import org.fdroid.fdroid.FDroid;
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
import org.fdroid.fdroid.Preferences;
|
import org.fdroid.fdroid.Preferences;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
|
import org.fdroid.fdroid.installer.PrivilegedInstaller;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
import eu.chainfire.libsuperuser.Shell;
|
import eu.chainfire.libsuperuser.Shell;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: This activity has no view on its own, it displays consecutive dialogs.
|
* 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";
|
private static final String TAG = "InstallIntoSystem";
|
||||||
|
|
||||||
public static final String ACTION_INSTALL = "install";
|
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_UNINSTALL = "uninstall";
|
||||||
public static final String ACTION_POST_INSTALL = "post_install";
|
public static final String ACTION_POST_INSTALL = "post_install";
|
||||||
public static final String ACTION_FIRST_TIME = "first_time";
|
public static final String ACTION_FIRST_TIME = "first_time";
|
||||||
|
|
||||||
String action;
|
String action;
|
||||||
|
String apkFile;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -69,6 +76,8 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apkFile = getIntent().getStringExtra(EXTRA_INSTALL_APK);
|
||||||
|
|
||||||
action = getIntent().getAction();
|
action = getIntent().getAction();
|
||||||
if (ACTION_UNINSTALL.equals(action)) {
|
if (ACTION_UNINSTALL.equals(action)) {
|
||||||
uninstall();
|
uninstall();
|
||||||
@ -85,8 +94,8 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
|
|||||||
if (Preferences.get().isFirstTime()) {
|
if (Preferences.get().isFirstTime()) {
|
||||||
Preferences.get().setFirstTime(false);
|
Preferences.get().setFirstTime(false);
|
||||||
|
|
||||||
if (Installer.hasSystemPermissions(context, context.getPackageManager())) {
|
if (PrivilegedInstaller.isAvailable(context)) {
|
||||||
Preferences.get().setSystemInstallerEnabled(true);
|
Preferences.get().setPrivilegedInstallerEnabled(true);
|
||||||
} else {
|
} else {
|
||||||
runFirstTime(context);
|
runFirstTime(context);
|
||||||
}
|
}
|
||||||
@ -112,8 +121,8 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
|
|||||||
if (false && probablyRoot) {
|
if (false && probablyRoot) {
|
||||||
// looks like we have root, at least su has a version number and is present
|
// looks like we have root, at least su has a version number and is present
|
||||||
|
|
||||||
Intent installIntent = new Intent(context, InstallIntoSystemDialogActivity.class);
|
Intent installIntent = new Intent(context, InstallPrivilegedDialogActivity.class);
|
||||||
installIntent.setAction(InstallIntoSystemDialogActivity.ACTION_FIRST_TIME);
|
installIntent.setAction(InstallPrivilegedDialogActivity.ACTION_FIRST_TIME);
|
||||||
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
|
||||||
PendingIntent resultPendingIntent =
|
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
|
// hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay
|
||||||
ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
|
ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
|
||||||
|
|
||||||
String message = getString(R.string.system_install_first_time_message) + "<br/><br/>" + InstallIntoSystem.create(getApplicationContext()).getWarningInfo();
|
String message = getString(R.string.system_install_first_time_message) + "<br/><br/>"
|
||||||
|
+ InstallPrivileged.create(getApplicationContext()).getWarningInfo();
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(theme)
|
AlertDialog.Builder builder = new AlertDialog.Builder(theme)
|
||||||
.setMessage(Html.fromHtml(message))
|
.setMessage(Html.fromHtml(message))
|
||||||
.setPositiveButton(R.string.system_permission_install_via_root, new DialogInterface.OnClickListener() {
|
.setPositiveButton(R.string.system_permission_install_via_root, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
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() {
|
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
InstallIntoSystemDialogActivity.this.setResult(Activity.RESULT_CANCELED);
|
InstallPrivilegedDialogActivity.this.setResult(Activity.RESULT_CANCELED);
|
||||||
InstallIntoSystemDialogActivity.this.finish();
|
InstallPrivilegedDialogActivity.this.finish();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.create().show();
|
builder.create().show();
|
||||||
@ -188,7 +202,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
|
|||||||
super.onPreExecute();
|
super.onPreExecute();
|
||||||
|
|
||||||
// hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay
|
// 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());
|
FDroidApp.getCurThemeResId());
|
||||||
|
|
||||||
mProgressDialog = new ProgressDialog(theme);
|
mProgressDialog = new ProgressDialog(theme);
|
||||||
@ -224,7 +238,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
|
|||||||
|
|
||||||
if (!ACTION_FIRST_TIME.equals(action)) {
|
if (!ACTION_FIRST_TIME.equals(action)) {
|
||||||
// hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay
|
// 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());
|
FDroidApp.getCurThemeResId());
|
||||||
|
|
||||||
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(theme)
|
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(theme)
|
||||||
@ -233,8 +247,8 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
|
|||||||
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
InstallIntoSystemDialogActivity.this.setResult(Activity.RESULT_CANCELED);
|
InstallPrivilegedDialogActivity.this.setResult(Activity.RESULT_CANCELED);
|
||||||
InstallIntoSystemDialogActivity.this.finish();
|
InstallPrivilegedDialogActivity.this.finish();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
alertBuilder.create().show();
|
alertBuilder.create().show();
|
||||||
@ -254,7 +268,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
|
|||||||
super.onPreExecute();
|
super.onPreExecute();
|
||||||
|
|
||||||
// hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay
|
// 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());
|
FDroidApp.getCurThemeResId());
|
||||||
|
|
||||||
mProgressDialog = new ProgressDialog(theme);
|
mProgressDialog = new ProgressDialog(theme);
|
||||||
@ -266,7 +280,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... voids) {
|
protected Void doInBackground(Void... voids) {
|
||||||
InstallIntoSystem.create(getApplicationContext()).runInstall();
|
InstallPrivileged.create(getApplicationContext()).runInstall(apkFile);
|
||||||
return null;
|
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
|
// hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay
|
||||||
ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
|
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
|
// enable system installer on installation success
|
||||||
Preferences.get().setSystemInstallerEnabled(success);
|
Preferences.get().setPrivilegedInstallerEnabled(success);
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(theme)
|
AlertDialog.Builder builder = new AlertDialog.Builder(theme)
|
||||||
.setTitle(success ? R.string.system_install_post_success : R.string.system_install_post_fail)
|
.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() {
|
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
InstallIntoSystemDialogActivity.this.setResult(success ? Activity.RESULT_OK : Activity.RESULT_CANCELED);
|
InstallPrivilegedDialogActivity.this.setResult(success ? Activity.RESULT_OK : Activity.RESULT_CANCELED);
|
||||||
InstallIntoSystemDialogActivity.this.finish();
|
InstallPrivilegedDialogActivity.this.finish();
|
||||||
startActivity(new Intent(InstallIntoSystemDialogActivity.this, FDroid.class));
|
startActivity(new Intent(InstallPrivilegedDialogActivity.this, FDroid.class));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.setCancelable(false);
|
.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
|
// hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay
|
||||||
ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
|
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)
|
AlertDialog.Builder builder = new AlertDialog.Builder(theme)
|
||||||
.setTitle(R.string.system_uninstall)
|
.setTitle(R.string.system_uninstall)
|
||||||
.setMessage(R.string.system_uninstall_message)
|
.setMessage(R.string.system_uninstall_message)
|
||||||
@ -317,8 +331,8 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
|
|||||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
InstallIntoSystemDialogActivity.this.setResult(Activity.RESULT_CANCELED);
|
InstallPrivilegedDialogActivity.this.setResult(Activity.RESULT_CANCELED);
|
||||||
InstallIntoSystemDialogActivity.this.finish();
|
InstallPrivilegedDialogActivity.this.finish();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.create().show();
|
builder.create().show();
|
||||||
@ -329,8 +343,8 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
|
|||||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
InstallIntoSystemDialogActivity.this.setResult(Activity.RESULT_CANCELED);
|
InstallPrivilegedDialogActivity.this.setResult(Activity.RESULT_CANCELED);
|
||||||
InstallIntoSystemDialogActivity.this.finish();
|
InstallPrivilegedDialogActivity.this.finish();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.create().show();
|
builder.create().show();
|
||||||
@ -345,7 +359,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
|
|||||||
super.onPreExecute();
|
super.onPreExecute();
|
||||||
|
|
||||||
// hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay
|
// 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());
|
FDroidApp.getCurThemeResId());
|
||||||
|
|
||||||
mProgressDialog = new ProgressDialog(theme);
|
mProgressDialog = new ProgressDialog(theme);
|
||||||
@ -357,7 +371,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... voids) {
|
protected Void doInBackground(Void... voids) {
|
||||||
InstallIntoSystem.create(getApplicationContext()).runUninstall();
|
InstallPrivileged.create(getApplicationContext()).runUninstall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -16,7 +16,7 @@
|
|||||||
** limitations under the License.
|
** limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.fdroid.fdroid.installer;
|
package org.fdroid.fdroid.privileged.views;
|
||||||
|
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
@ -25,10 +25,10 @@ import android.net.Uri;
|
|||||||
|
|
||||||
public class AppDiff {
|
public class AppDiff {
|
||||||
|
|
||||||
final PackageManager mPm;
|
public final PackageManager mPm;
|
||||||
final PackageInfo mPkgInfo;
|
public final PackageInfo mPkgInfo;
|
||||||
|
|
||||||
ApplicationInfo mInstalledAppInfo = null;
|
public ApplicationInfo mInstalledAppInfo = null;
|
||||||
|
|
||||||
public AppDiff(PackageManager mPm, Uri mPackageURI) {
|
public AppDiff(PackageManager mPm, Uri mPackageURI) {
|
||||||
this.mPm = mPm;
|
this.mPm = mPm;
|
@ -15,7 +15,7 @@
|
|||||||
** See the License for the specific language governing permissions and
|
** See the License for the specific language governing permissions and
|
||||||
** limitations under the License.
|
** limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.fdroid.fdroid.installer;
|
package org.fdroid.fdroid.privileged.views;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
@ -15,7 +15,7 @@
|
|||||||
** limitations under the License.
|
** limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.fdroid.fdroid.installer;
|
package org.fdroid.fdroid.privileged.views;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
@ -16,7 +16,7 @@
|
|||||||
** limitations under the License.
|
** limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.fdroid.fdroid.installer;
|
package org.fdroid.fdroid.privileged.views;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
@ -14,7 +14,7 @@
|
|||||||
** See the License for the specific language governing permissions and
|
** See the License for the specific language governing permissions and
|
||||||
** limitations under the License.
|
** limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.fdroid.fdroid.installer;
|
package org.fdroid.fdroid.privileged.views;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
@ -14,13 +14,14 @@ import android.support.v7.app.AlertDialog;
|
|||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.AppDetails;
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
import org.fdroid.fdroid.Preferences;
|
import org.fdroid.fdroid.Preferences;
|
||||||
import org.fdroid.fdroid.PreferencesActivity;
|
import org.fdroid.fdroid.PreferencesActivity;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
import org.fdroid.fdroid.installer.InstallIntoSystemDialogActivity;
|
import org.fdroid.fdroid.installer.PrivilegedInstaller;
|
||||||
import org.fdroid.fdroid.installer.Installer;
|
import org.fdroid.fdroid.privileged.install.InstallPrivilegedDialogActivity;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ public class PreferencesFragment extends PreferenceFragment
|
|||||||
Preferences.PREF_LANGUAGE,
|
Preferences.PREF_LANGUAGE,
|
||||||
Preferences.PREF_CACHE_APK,
|
Preferences.PREF_CACHE_APK,
|
||||||
Preferences.PREF_EXPERT,
|
Preferences.PREF_EXPERT,
|
||||||
Preferences.PREF_SYSTEM_INSTALLER,
|
Preferences.PREF_PRIVILEGED_INSTALLER,
|
||||||
Preferences.PREF_ENABLE_PROXY,
|
Preferences.PREF_ENABLE_PROXY,
|
||||||
Preferences.PREF_PROXY_HOST,
|
Preferences.PREF_PROXY_HOST,
|
||||||
Preferences.PREF_PROXY_PORT,
|
Preferences.PREF_PROXY_PORT,
|
||||||
@ -149,7 +150,7 @@ public class PreferencesFragment extends PreferenceFragment
|
|||||||
checkSummary(key, R.string.expert_on);
|
checkSummary(key, R.string.expert_on);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Preferences.PREF_SYSTEM_INSTALLER:
|
case Preferences.PREF_PRIVILEGED_INSTALLER:
|
||||||
checkSummary(key, R.string.system_installer_on);
|
checkSummary(key, R.string.system_installer_on);
|
||||||
break;
|
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
|
* Initializes SystemInstaller preference, which can only be enabled when F-Droid is installed as a system-app
|
||||||
*/
|
*/
|
||||||
protected void initSystemInstallerPreference() {
|
protected void initPrivilegedInstallerPreference() {
|
||||||
CheckBoxPreference pref = (CheckBoxPreference) findPreference(Preferences.PREF_SYSTEM_INSTALLER);
|
CheckBoxPreference pref = (CheckBoxPreference) findPreference(Preferences.PREF_PRIVILEGED_INSTALLER);
|
||||||
|
|
||||||
// we are handling persistence ourself!
|
// we are handling persistence ourself!
|
||||||
pref.setPersistent(false);
|
pref.setPersistent(false);
|
||||||
@ -195,16 +196,16 @@ public class PreferencesFragment extends PreferenceFragment
|
|||||||
final CheckBoxPreference pref = (CheckBoxPreference) preference;
|
final CheckBoxPreference pref = (CheckBoxPreference) preference;
|
||||||
|
|
||||||
if (pref.isChecked()) {
|
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
|
// system-permission are granted, i.e. F-Droid is a system-app
|
||||||
SharedPreferences.Editor editor = pref.getSharedPreferences().edit();
|
SharedPreferences.Editor editor = pref.getSharedPreferences().edit();
|
||||||
editor.putBoolean(Preferences.PREF_SYSTEM_INSTALLER, true);
|
editor.putBoolean(Preferences.PREF_PRIVILEGED_INSTALLER, true);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
pref.setChecked(true);
|
pref.setChecked(true);
|
||||||
} else {
|
} else {
|
||||||
// system-permission not available
|
// system-permission not available
|
||||||
SharedPreferences.Editor editor = pref.getSharedPreferences().edit();
|
SharedPreferences.Editor editor = pref.getSharedPreferences().edit();
|
||||||
editor.putBoolean(Preferences.PREF_SYSTEM_INSTALLER, false);
|
editor.putBoolean(Preferences.PREF_PRIVILEGED_INSTALLER, false);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
pref.setChecked(false);
|
pref.setChecked(false);
|
||||||
|
|
||||||
@ -221,9 +222,11 @@ public class PreferencesFragment extends PreferenceFragment
|
|||||||
alertBuilder.setPositiveButton(R.string.system_permission_install_via_root, new DialogInterface.OnClickListener() {
|
alertBuilder.setPositiveButton(R.string.system_permission_install_via_root, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
Intent installIntent = new Intent(getActivity(), InstallIntoSystemDialogActivity.class);
|
// Open details of F-Droid Privileged
|
||||||
installIntent.setAction(InstallIntoSystemDialogActivity.ACTION_INSTALL);
|
Intent intent = new Intent(getActivity(), AppDetails.class);
|
||||||
startActivity(installIntent);
|
intent.putExtra(AppDetails.EXTRA_APPID,
|
||||||
|
PrivilegedInstaller.PRIVILEGED_PACKAGE_NAME);
|
||||||
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
alertBuilder.setNegativeButton(R.string.cancel, null);
|
alertBuilder.setNegativeButton(R.string.cancel, null);
|
||||||
@ -231,7 +234,7 @@ public class PreferencesFragment extends PreferenceFragment
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
SharedPreferences.Editor editor = pref.getSharedPreferences().edit();
|
SharedPreferences.Editor editor = pref.getSharedPreferences().edit();
|
||||||
editor.putBoolean(Preferences.PREF_SYSTEM_INSTALLER, false);
|
editor.putBoolean(Preferences.PREF_PRIVILEGED_INSTALLER, false);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
pref.setChecked(false);
|
pref.setChecked(false);
|
||||||
}
|
}
|
||||||
@ -241,16 +244,16 @@ public class PreferencesFragment extends PreferenceFragment
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initUninstallSystemAppPreference() {
|
protected void initUninstallPrivilegedAppPreference() {
|
||||||
Preference pref = findPreference(Preferences.PREF_UNINSTALL_SYSTEM_APP);
|
Preference pref = findPreference(Preferences.PREF_UNINSTALL_PRIVILEGED_APP);
|
||||||
pref.setPersistent(false);
|
pref.setPersistent(false);
|
||||||
|
|
||||||
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
Intent uninstallIntent = new Intent(getActivity(), InstallIntoSystemDialogActivity.class);
|
Intent uninstallIntent = new Intent(getActivity(), InstallPrivilegedDialogActivity.class);
|
||||||
uninstallIntent.setAction(InstallIntoSystemDialogActivity.ACTION_UNINSTALL);
|
uninstallIntent.setAction(InstallPrivilegedDialogActivity.ACTION_UNINSTALL);
|
||||||
startActivity(uninstallIntent);
|
startActivity(uninstallIntent);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -280,8 +283,8 @@ public class PreferencesFragment extends PreferenceFragment
|
|||||||
updateSummary(key, false);
|
updateSummary(key, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
initSystemInstallerPreference();
|
initPrivilegedInstallerPreference();
|
||||||
initUninstallSystemAppPreference();
|
initUninstallPrivilegedAppPreference();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
359
media/fdroid-logo-2015/fdroid-logo-privileged.svg
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
viewBox="0 0 48.000001 48.000001"
|
||||||
|
id="svg4230"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.5 r10040"
|
||||||
|
sodipodi:docname="fdroid-logo-privileged.svg"
|
||||||
|
inkscape:export-filename="/home/schuerm/Projekte/F-Droid/fdroidclient/F-Droid-Privileged/src/main/res/drawable-xxxhdpi/ic_launcher.png"
|
||||||
|
inkscape:export-xdpi="360"
|
||||||
|
inkscape:export-ydpi="360">
|
||||||
|
<defs
|
||||||
|
id="defs4232">
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient5212">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.09803922"
|
||||||
|
offset="0"
|
||||||
|
id="stop5214" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0"
|
||||||
|
offset="1"
|
||||||
|
id="stop5216" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient5212"
|
||||||
|
id="radialGradient5220"
|
||||||
|
cx="-98.23381"
|
||||||
|
cy="3.4695871"
|
||||||
|
fx="-98.23381"
|
||||||
|
fy="3.4695871"
|
||||||
|
r="22.671185"
|
||||||
|
gradientTransform="matrix(0,1.5082874,-1.6170977,3.0386328e-8,6.9414178,1153.7641)"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
style="color-interpolation-filters:sRGB"
|
||||||
|
id="filter4175"
|
||||||
|
x="-0.023846937"
|
||||||
|
width="1.0476939"
|
||||||
|
y="-0.02415504"
|
||||||
|
height="1.0483101">
|
||||||
|
<feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.45053152"
|
||||||
|
id="feGaussianBlur4177" />
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="5.656854"
|
||||||
|
inkscape:cx="35.90784"
|
||||||
|
inkscape:cy="40.28609"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="true"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="1918"
|
||||||
|
inkscape:window-height="1179"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="19"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
gridtolerance="10000" />
|
||||||
|
<metadata
|
||||||
|
id="metadata4235">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/3.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-1004.3622)">
|
||||||
|
<path
|
||||||
|
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#263238;fill-opacity:0.4;fill-rule:evenodd;stroke:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4175);enable-background:accumulate;font-family:sans-serif"
|
||||||
|
d="m 2.613462,1006.3488 a 1.250125,1.250125 0 0 0 -1.01172,2.0293 l 3.60351,4.6641 c -0.12699,0.3331 -0.20312,0.6915 -0.20312,1.0703 l 0,4 0,2.8652 0,0.1348 c 0,1.662 1.338,3 3,3 l 32,0 c 1.662,0 3,-1.338 3,-3 l 0,-4 0,-2.8652 0,-0.1348 c 0,-0.3803 -0.0771,-0.74 -0.20508,-1.0742 l 3.60156,-4.6602 a 1.250125,1.250125 0 0 0 -1.04882,-2.0273 1.250125,1.250125 0 0 0 -0.92969,0.498 l -3.43164,4.4414 c -0.31022,-0.1079 -0.63841,-0.1777 -0.98633,-0.1777 l -32,0 c -0.34857,0 -0.67757,0.069 -0.98828,0.1777 l -3.4336,-4.4414 a 1.250125,1.250125 0 0 0 -0.96679,-0.5 z m 5.38867,18.7637 c -0.20775,0 -0.40983,0.021 -0.60547,0.061 -1.36951,0.2761 -2.39453,1.4698 -2.39453,2.9101 l 0,0.029 0,19.7793 0,0.029 0,0.1914 c 0,1.662 1.338,3 3,3 l 32,0 c 1.662,0 3,-1.338 3,-3 l 0,-20 0,-0.029 c 0,-1.4403 -1.02502,-2.634 -2.39453,-2.9101 -0.19565,-0.039 -0.39772,-0.061 -0.60547,-0.061 l -32,0 z"
|
||||||
|
id="path4192"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
transform="matrix(0.7637817,0,0,0.7637817,0.31389533,237.54071)" />
|
||||||
|
<g
|
||||||
|
id="g5012"
|
||||||
|
transform="matrix(0.7637817,0,0,0.7637817,0.31389533,237.54071)">
|
||||||
|
<g
|
||||||
|
id="g4179"
|
||||||
|
transform="matrix(-1,0,0,1,47.999779,0)">
|
||||||
|
<path
|
||||||
|
style="fill:#8ab000;fill-opacity:1;fill-rule:evenodd;stroke:#769616;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 2.5889342,1006.8622 4.25,5.5"
|
||||||
|
id="path4181"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="cccccc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4183"
|
||||||
|
d="m 2.6113281,1005.6094 c -0.4534623,0.012 -0.7616975,0.189 -0.9807462,0.4486 2.0269314,2.4089 2.368401,2.7916 5.1354735,6.2214 1.0195329,1.319 2.0816026,0.6373 1.0620696,-0.6817 l -4.25,-5.5 c -0.2289894,-0.3056 -0.5850813,-0.478 -0.9667969,-0.4883 z"
|
||||||
|
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ffffff;fill-opacity:0.29803922;fill-rule:evenodd;stroke:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:sans-serif" />
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="ccccc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4185"
|
||||||
|
d="m 1.6220992,1006.0705 c -0.1238933,0.1479 -0.561176,0.8046 -0.02249,1.5562 l 4.25,5.5 c 1.0195329,1.319 1.1498748,-0.6123 1.1498748,-0.6123 0,0 -3.7344514,-4.51 -5.3773848,-6.4439 z"
|
||||||
|
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#263238;fill-opacity:0.2;fill-rule:evenodd;stroke:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:sans-serif" />
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="cscccc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4187"
|
||||||
|
d="m 2.3378905,1005.8443 c -0.438175,0 -0.959862,0.1416 -0.8242183,0.7986 0.103561,0.5016 4.6608262,6.0744 4.6608262,6.0744 1.0195329,1.319 2.4934721,0.6763 1.4739391,-0.6425 l -4.234375,-5.4727 c -0.2602394,-0.29 -0.6085188,-0.7436 -1.076172,-0.7578 z"
|
||||||
|
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#8ab000;fill-opacity:1;fill-rule:evenodd;stroke:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:sans-serif" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g4955">
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="cc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4945"
|
||||||
|
d="m 2.5889342,1006.8622 4.25,5.5"
|
||||||
|
style="fill:#8ab000;fill-opacity:1;fill-rule:evenodd;stroke:#769616;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||||
|
<path
|
||||||
|
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ffffff;fill-opacity:0.29803922;fill-rule:evenodd;stroke:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:sans-serif"
|
||||||
|
d="m 2.6113281,1005.6094 c -0.4534623,0.012 -0.7616975,0.189 -0.9807462,0.4486 2.0269314,2.4089 2.368401,2.7916 5.1354735,6.2214 1.0195329,1.319 2.0816026,0.6373 1.0620696,-0.6817 l -4.25,-5.5 c -0.2289894,-0.3056 -0.5850813,-0.478 -0.9667969,-0.4883 z"
|
||||||
|
id="path4947"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccccc" />
|
||||||
|
<path
|
||||||
|
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#263238;fill-opacity:0.2;fill-rule:evenodd;stroke:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:sans-serif"
|
||||||
|
d="m 1.6220992,1006.0705 c -0.1238933,0.1479 -0.561176,0.8046 -0.02249,1.5562 l 4.25,5.5 c 1.0195329,1.319 1.1498748,-0.6123 1.1498748,-0.6123 0,0 -3.7344514,-4.51 -5.3773848,-6.4439 z"
|
||||||
|
id="path4951"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<path
|
||||||
|
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#8ab000;fill-opacity:1;fill-rule:evenodd;stroke:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:sans-serif"
|
||||||
|
d="m 2.3378905,1005.8443 c -0.438175,0 -0.959862,0.1416 -0.8242183,0.7986 0.103561,0.5016 4.6608262,6.0744 4.6608262,6.0744 1.0195329,1.319 2.4934721,0.6763 1.4739391,-0.6425 l -4.234375,-5.4727 c -0.2602394,-0.29 -0.6085188,-0.7436 -1.076172,-0.7578 z"
|
||||||
|
id="path4925"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cscccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(42,0)"
|
||||||
|
id="g4967">
|
||||||
|
<rect
|
||||||
|
style="fill:#aeea00;fill-opacity:1;stroke:none"
|
||||||
|
id="rect4144"
|
||||||
|
width="38"
|
||||||
|
height="13"
|
||||||
|
x="-37"
|
||||||
|
y="1010.3622"
|
||||||
|
rx="3"
|
||||||
|
ry="3" />
|
||||||
|
<rect
|
||||||
|
ry="3"
|
||||||
|
rx="3"
|
||||||
|
y="1013.3622"
|
||||||
|
x="-37"
|
||||||
|
height="10"
|
||||||
|
width="38"
|
||||||
|
id="rect4961"
|
||||||
|
style="fill:#263238;fill-opacity:0.2;stroke:none" />
|
||||||
|
<rect
|
||||||
|
ry="3"
|
||||||
|
rx="3"
|
||||||
|
y="1010.3622"
|
||||||
|
x="-37"
|
||||||
|
height="10"
|
||||||
|
width="38"
|
||||||
|
id="rect4963"
|
||||||
|
style="fill:#ffffff;fill-opacity:0.29803922;stroke:none" />
|
||||||
|
<rect
|
||||||
|
ry="2.5384617"
|
||||||
|
rx="3"
|
||||||
|
y="1011.3622"
|
||||||
|
x="-37"
|
||||||
|
height="11"
|
||||||
|
width="38"
|
||||||
|
id="rect4965"
|
||||||
|
style="fill:#aeea00;fill-opacity:1;stroke:none" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g4979">
|
||||||
|
<rect
|
||||||
|
style="fill:#1976d2;fill-opacity:1;stroke:none"
|
||||||
|
id="rect4146"
|
||||||
|
width="38"
|
||||||
|
height="26"
|
||||||
|
x="5"
|
||||||
|
y="1024.3622"
|
||||||
|
rx="3"
|
||||||
|
ry="3" />
|
||||||
|
<rect
|
||||||
|
ry="3"
|
||||||
|
rx="3"
|
||||||
|
y="1037.3622"
|
||||||
|
x="5"
|
||||||
|
height="13"
|
||||||
|
width="38"
|
||||||
|
id="rect4973"
|
||||||
|
style="fill:#263238;fill-opacity:0.2;stroke:none" />
|
||||||
|
<rect
|
||||||
|
ry="3"
|
||||||
|
rx="3"
|
||||||
|
y="1024.3622"
|
||||||
|
x="5"
|
||||||
|
height="13"
|
||||||
|
width="38"
|
||||||
|
id="rect4975"
|
||||||
|
style="fill:#ffffff;fill-opacity:0.2;stroke:none" />
|
||||||
|
<rect
|
||||||
|
ry="2.7692308"
|
||||||
|
rx="3"
|
||||||
|
y="1025.3622"
|
||||||
|
x="5"
|
||||||
|
height="24"
|
||||||
|
width="38"
|
||||||
|
id="rect4977"
|
||||||
|
style="fill:#1976d2;fill-opacity:1;stroke:none" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(0,1013.3622)"
|
||||||
|
id="g4211">
|
||||||
|
<path
|
||||||
|
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#0d47a1;fill-opacity:1;fill-rule:nonzero;stroke:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:sans-serif"
|
||||||
|
d="m 24,17.75 c -2.880662,0 -5.319789,1.984685 -6.033203,4.650391 l 3.212891,0 C 21.734004,21.415044 22.774798,20.75 24,20.75 c 1.812692,0 3.25,1.437308 3.25,3.25 0,1.812693 -1.437308,3.25 -3.25,3.25 -1.307381,0 -2.411251,-0.75269 -2.929688,-1.849609 l -3.154296,0 C 18.558263,28.166146 21.04791,30.25 24,30.25 c 3.434013,0 6.25,-2.815987 6.25,-6.25 0,-3.434012 -2.815987,-6.25 -6.25,-6.25 z"
|
||||||
|
id="path4161"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#0d47a1;stroke-width:1.89999998;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="path4209"
|
||||||
|
cx="24"
|
||||||
|
cy="24"
|
||||||
|
r="9.5500002"
|
||||||
|
d="m 33.55,24 c 0,5.274319 -4.275681,9.55 -9.55,9.55 -5.274319,0 -9.55,-4.275681 -9.55,-9.55 0,-5.274319 4.275681,-9.55 9.55,-9.55 5.274319,0 9.55,4.275681 9.55,9.55 z"
|
||||||
|
sodipodi:cx="24"
|
||||||
|
sodipodi:cy="24"
|
||||||
|
sodipodi:rx="9.5500002"
|
||||||
|
sodipodi:ry="9.5500002" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g4989"
|
||||||
|
transform="translate(0,0.50001738)">
|
||||||
|
<ellipse
|
||||||
|
cy="1016.4872"
|
||||||
|
cx="14.375"
|
||||||
|
id="circle4985"
|
||||||
|
style="fill:#263238;fill-opacity:0.2;stroke:none"
|
||||||
|
rx="3.375"
|
||||||
|
ry="3.875"
|
||||||
|
d="m 17.75,1016.4872 c 0,2.1401 -1.511039,3.875 -3.375,3.875 -1.863961,0 -3.375,-1.7349 -3.375,-3.875 0,-2.1401 1.511039,-3.875 3.375,-3.875 1.863961,0 3.375,1.7349 3.375,3.875 z"
|
||||||
|
sodipodi:cx="14.375"
|
||||||
|
sodipodi:cy="1016.4872"
|
||||||
|
sodipodi:rx="3.375"
|
||||||
|
sodipodi:ry="3.875" />
|
||||||
|
<circle
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||||
|
id="path4859"
|
||||||
|
cx="14.375"
|
||||||
|
cy="1016.9872"
|
||||||
|
r="3.375"
|
||||||
|
d="m 17.75,1016.9872 c 0,1.8639 -1.511039,3.375 -3.375,3.375 -1.863961,0 -3.375,-1.5111 -3.375,-3.375 0,-1.864 1.511039,-3.375 3.375,-3.375 1.863961,0 3.375,1.511 3.375,3.375 z"
|
||||||
|
sodipodi:cx="14.375"
|
||||||
|
sodipodi:cy="1016.9872"
|
||||||
|
sodipodi:rx="3.375"
|
||||||
|
sodipodi:ry="3.375" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(19.5,0.50001738)"
|
||||||
|
id="g4171">
|
||||||
|
<ellipse
|
||||||
|
ry="3.875"
|
||||||
|
rx="3.375"
|
||||||
|
style="fill:#263238;fill-opacity:0.2;stroke:none"
|
||||||
|
id="ellipse4175"
|
||||||
|
cx="14.375"
|
||||||
|
cy="1016.4872"
|
||||||
|
d="m 17.75,1016.4872 c 0,2.1401 -1.511039,3.875 -3.375,3.875 -1.863961,0 -3.375,-1.7349 -3.375,-3.875 0,-2.1401 1.511039,-3.875 3.375,-3.875 1.863961,0 3.375,1.7349 3.375,3.875 z"
|
||||||
|
sodipodi:cx="14.375"
|
||||||
|
sodipodi:cy="1016.4872"
|
||||||
|
sodipodi:rx="3.375"
|
||||||
|
sodipodi:ry="3.875" />
|
||||||
|
<circle
|
||||||
|
r="3.375"
|
||||||
|
cy="1016.9872"
|
||||||
|
cx="14.375"
|
||||||
|
id="circle4177"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||||
|
d="m 17.75,1016.9872 c 0,1.8639 -1.511039,3.375 -3.375,3.375 -1.863961,0 -3.375,-1.5111 -3.375,-3.375 0,-1.864 1.511039,-3.375 3.375,-3.375 1.863961,0 3.375,1.511 3.375,3.375 z"
|
||||||
|
sodipodi:cx="14.375"
|
||||||
|
sodipodi:cy="1016.9872"
|
||||||
|
sodipodi:rx="3.375"
|
||||||
|
sodipodi:ry="3.375" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path5128"
|
||||||
|
d="m 2.3100098,1005.5986 a 0.95482259,0.95482259 0 0 0 -0.7727332,1.5499 l 2.7522949,3.5624 c -0.096993,0.2544 -0.1551393,0.5281 -0.1551393,0.8175 l 0,3.0551 0,2.1884 0,0.1029 c 0,1.2694 1.0219399,2.2914 2.2913451,2.2914 l 24.4410147,0 c 1.269405,0 2.291345,-1.022 2.291345,-2.2914 l 0,-3.0551 0,-2.1884 0,-0.1029 c 0,-0.2905 -0.05889,-0.5652 -0.156637,-0.8205 l 2.750806,-3.5594 a 0.95482259,0.95482259 0 0 0 -0.80107,-1.5484 0.95482259,0.95482259 0 0 0 -0.71008,0.3804 l -2.621024,3.3922 c -0.23694,-0.082 -0.487605,-0.1357 -0.75334,-0.1357 l -24.4410147,0 c -0.2662314,0 -0.5175156,0.053 -0.7548302,0.1357 l -2.6225208,-3.3922 a 0.95482259,0.95482259 0 0 0 -0.7384165,-0.3819 z m 4.1157675,14.3314 c -0.1586756,0 -0.3130207,0.016 -0.4624469,0.047 -1.0460067,0.2109 -1.8288982,1.1227 -1.8288982,2.2227 l 0,0.022 0,15.107 0,0.022 0,0.1462 c 0,1.2694 1.0219399,2.2913 2.2913451,2.2913 l 24.4410147,0 c 1.269405,0 2.291345,-1.0219 2.291345,-2.2913 l 0,-15.2756 0,-0.022 c 0,-1.1001 -0.782892,-2.0118 -1.828898,-2.2227 -0.149434,-0.03 -0.303772,-0.047 -0.462447,-0.047 l -24.4410147,0 z"
|
||||||
|
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:url(#radialGradient5220);fill-opacity:1;fill-rule:evenodd;stroke:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:sans-serif" />
|
||||||
|
<g
|
||||||
|
id="g5105"
|
||||||
|
transform="translate(-63.462837,-10.429826)">
|
||||||
|
<path
|
||||||
|
style="fill:#aeea00;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 106.34739,1049.0525 h -1.55682 v -4.1515 c 0,-1.1521 -0.9341,-2.0758 -2.07577,-2.0758 h -4.151519 v -1.5568 a 2.5947022,2.5947022 0 0 0 -2.594702,-2.5947 2.5947022,2.5947022 0 0 0 -2.594702,2.5947 v 1.5568 h -4.151522 a 2.0757614,2.0757614 0 0 0 -2.075761,2.0758 v 3.9439 h 1.556821 c 1.55682,0 2.802277,1.2455 2.802277,2.8023 0,1.5568 -1.245457,2.8023 -2.802277,2.8023 h -1.556821 v 3.9439 a 2.0757614,2.0757614 0 0 0 2.075761,2.0758 h 3.943946 v -1.5568 c 0,-1.5568 1.245457,-2.8023 2.802278,-2.8023 1.556821,0 2.802278,1.2455 2.802278,2.8023 v 1.5568 h 3.943943 a 2.0757614,2.0757614 0 0 0 2.07577,-2.0758 v -4.1515 h 1.55682 a 2.5947022,2.5947022 0 0 0 2.5947,-2.5947 2.5947022,2.5947022 0 0 0 -2.5947,-2.5947 z"
|
||||||
|
id="path4142-6" />
|
||||||
|
<path
|
||||||
|
style="fill:#263238;fill-opacity:0.2"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 106.29783,1048.977 h -1.55682 v -4.1515 c 0,-1.1521 -0.93409,-2.0758 -2.07576,-2.0758 h -4.151525 v -1.5568 a 2.5947023,2.5947023 0 0 0 -2.594702,-2.5947 2.5947023,2.5947023 0 0 0 -2.594702,2.5947 v 1.5568 h -4.151522 a 2.0757614,2.0757614 0 0 0 -2.075761,2.0758 v 3.9439 h 1.556821 c 1.55682,0 2.802277,1.2455 2.802277,2.8023 0,1.5568 -1.245457,2.8023 -2.802277,2.8023 h -1.556821 v 3.9439 a 2.0757614,2.0757614 0 0 0 2.075761,2.0758 h 3.943946 v -1.5568 c 0,-1.5568 1.245457,-2.8023 2.802278,-2.8023 1.556821,0 2.802278,1.2455 2.802278,2.8023 v 1.5568 h 3.943949 a 2.0757614,2.0757614 0 0 0 2.07576,-2.0758 v -4.1515 h 1.55682 a 2.5947023,2.5947023 0 0 0 2.5947,-2.5947 2.5947023,2.5947023 0 0 0 -2.5947,-2.5947 z"
|
||||||
|
id="path4142-6-5" />
|
||||||
|
<path
|
||||||
|
style="fill:#aeea00;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 104.63448,1049.1066 h -1.43578 v -3.8287 c 0,-1.0625 -0.86147,-1.9144 -1.91438,-1.9144 h -3.828749 v -1.4357 a 2.3929704,2.3929704 0 0 0 -2.39297,-2.393 2.3929704,2.3929704 0 0 0 -2.392971,2.393 v 1.4357 h -3.828751 a 1.9143763,1.9143763 0 0 0 -1.914376,1.9144 v 3.6374 h 1.435782 c 1.435781,0 2.584407,1.1486 2.584407,2.5843 0,1.4359 -1.148626,2.5845 -2.584407,2.5845 h -1.435782 v 3.6373 a 1.9143763,1.9143763 0 0 0 1.914376,1.9144 h 3.637314 v -1.4359 c 0,-1.4357 1.148626,-2.5843 2.584408,-2.5843 1.435782,0 2.584408,1.1486 2.584408,2.5843 v 1.4359 h 3.637311 a 1.9143763,1.9143763 0 0 0 1.91438,-1.9144 v -3.8287 h 1.43578 a 2.3929704,2.3929704 0 0 0 2.39297,-2.3931 2.3929704,2.3929704 0 0 0 -2.39297,-2.393 z"
|
||||||
|
id="path4142" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 21 KiB |
1
media/fdroid-logo-2015/puzzle.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M20.5,11H19V7C19,5.89 18.1,5 17,5H13V3.5A2.5,2.5 0 0,0 10.5,1A2.5,2.5 0 0,0 8,3.5V5H4A2,2 0 0,0 2,7V10.8H3.5C5,10.8 6.2,12 6.2,13.5C6.2,15 5,16.2 3.5,16.2H2V20A2,2 0 0,0 4,22H7.8V20.5C7.8,19 9,17.8 10.5,17.8C12,17.8 13.2,19 13.2,20.5V22H17A2,2 0 0,0 19,20V16H20.5A2.5,2.5 0 0,0 23,13.5A2.5,2.5 0 0,0 20.5,11Z" /></svg>
|
After Width: | Height: | Size: 603 B |
18
privileged-api-lib/build.gradle
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
6
privileged-api-lib/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="org.fdroid.fdroid.privileged.api">
|
||||||
|
|
||||||
|
<application />
|
||||||
|
|
||||||
|
</manifest>
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
interface IPrivilegedCallback {
|
||||||
|
|
||||||
|
void handleResult(in String packageName, in int returnCode);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
include ':F-Droid'
|
include ':F-Droid'
|
||||||
|
include ':F-Droid-Privileged'
|
||||||
|
include ':privileged-api-lib'
|
||||||
if (hasProperty('sourceDeps')) {
|
if (hasProperty('sourceDeps')) {
|
||||||
include ':extern:support-v4-preferencefragment'
|
include ':extern:support-v4-preferencefragment'
|
||||||
include ':extern:nanohttpd:core'
|
include ':extern:nanohttpd:core'
|
||||||
|