diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 620575958..955db353c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3,7 +3,7 @@
package="org.fdroid.fdroid"
android:installLocation="auto"
android:versionCode="640"
- android:versionName="0.64-test" >
+ android:versionName="0.64-test" xmlns:tools="http://schemas.android.com/tools">
-
-
-
-
+
+
+
+
-
-
-
diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java
index 4c90cc83c..ca1d0a707 100644
--- a/src/org/fdroid/fdroid/AppDetails.java
+++ b/src/org/fdroid/fdroid/AppDetails.java
@@ -21,7 +21,10 @@ package org.fdroid.fdroid;
import android.content.*;
import android.widget.*;
+
import org.fdroid.fdroid.data.*;
+import org.fdroid.fdroid.installer.Installer;
+import org.fdroid.fdroid.installer.Installer.AndroidNotCompatibleException;
import org.xml.sax.XMLReader;
import android.app.AlertDialog;
@@ -253,7 +256,7 @@ public class AppDetails extends ListActivity {
private final Context mctx = this;
private DisplayImageOptions displayImageOptions;
- private InstallManager installManager;
+ private Installer installer;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -307,7 +310,7 @@ public class AppDetails extends ListActivity {
}
mPm = getPackageManager();
- installManager = new InstallManager(this, mPm, myInstallCallback);
+ installer = Installer.getActivityInstaller(this, mPm, myInstallCallback);
// Get the preferences we're going to use in this Activity...
AppDetails old = (AppDetails) getLastNonConfigurationInstance();
@@ -926,16 +929,24 @@ public class AppDetails extends ListActivity {
Utils.getApkCacheDir(getBaseContext()));
}
- private void installApk(File file, String id) {
- installManager.installApk(file, id);
+ private void installApk(File file, String packageName) {
+ try {
+ installer.installPackage(file);
+ } catch (AndroidNotCompatibleException e) {
+ Log.e(TAG, "Android not compatible with this Installer!", e);
+ }
- notifyAppChanged(id);
+ notifyAppChanged(packageName);
}
- private void removeApk(String id) {
- installManager.removeApk(id);
+ private void removeApk(String packageName) {
+ try {
+ installer.deletePackage(packageName);
+ } catch (AndroidNotCompatibleException e) {
+ Log.e(TAG, "Android not compatible with this Installer!", e);
+ }
- notifyAppChanged(id);
+ notifyAppChanged(packageName);
}
/**
@@ -946,7 +957,7 @@ public class AppDetails extends ListActivity {
getContentResolver().notifyChange(AppProvider.getContentUri(id), null);
}
- private InstallManager.InstallCallback myInstallCallback = new InstallManager.InstallCallback() {
+ private Installer.InstallerCallback myInstallCallback = new Installer.InstallerCallback() {
@Override
public void onPackageInstalled(int returnCode, boolean unattended) {
@@ -1156,12 +1167,15 @@ public class AppDetails extends ListActivity {
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // handle cases for install manager first
+ if (installer.handleOnActivityResult(requestCode, resultCode, data)) {
+ return;
+ }
+
switch (requestCode) {
case REQUEST_ENABLE_BLUETOOTH:
fdroidApp.sendViaBluetooth(this, resultCode, app.id);
break;
- default:
- installManager.handleOnActivityResult(requestCode, resultCode, data);
}
}
}
diff --git a/src/org/fdroid/fdroid/InstallManager.java b/src/org/fdroid/fdroid/InstallManager.java
deleted file mode 100644
index 9707af47b..000000000
--- a/src/org/fdroid/fdroid/InstallManager.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 3
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
- */
-
-package org.fdroid.fdroid;
-
-import java.io.File;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.net.Uri;
-import android.os.Build;
-import android.util.Log;
-
-public class InstallManager {
- private Activity mActivity;
- private PackageManager mPm;
-
- private InstallCallback mInstallCallback;
-
- public static final String TAG = "FDroid";
-
- private static final int REQUEST_INSTALL = 0;
- private static final int REQUEST_UNINSTALL = 1;
-
- public interface InstallCallback {
-
- public static final int RETURN_SUCCESS = 1;
- public static final int RETURN_CANCEL = 0;
-
- public void onPackageInstalled(int returnCode, boolean unattended);
-
- public void onPackageDeleted(int returnCode, boolean unattended);
- }
-
- public InstallManager(Activity activity, PackageManager pm, InstallCallback installCallback) {
- super();
- this.mActivity = activity;
- this.mPm = pm;
- this.mInstallCallback = installCallback;
- }
-
- public void removeApk(String id) {
- PackageInfo pkginfo;
- try {
- pkginfo = mPm.getPackageInfo(id, 0);
- } catch (NameNotFoundException e) {
- Log.d(TAG, "Couldn't find package " + id + " to uninstall.");
- return;
- }
-
- // try unattended delete using internal API. This only works when F-Droid is installed as
- // system app
- try {
- final InstallSystemManager systemInstall = new InstallSystemManager(mActivity,
- mySystemCallback);
- systemInstall.deletePackage(pkginfo.packageName);
-
- return;
- } catch (Exception e) {
- Log.d(TAG, "Unattended delete failed, falling back to normal delete method...", e);
- }
-
- Uri uri = Uri.fromParts("package", pkginfo.packageName, null);
- Intent intent = new Intent(Intent.ACTION_DELETE, uri);
- mActivity.startActivityForResult(intent, REQUEST_UNINSTALL);
- }
-
- public void installApk(File file, String id) {
- // try unattended install using internal API. This only works when F-Droid is installed as
- // system app
- try {
- final InstallSystemManager systemInstall = new InstallSystemManager(mActivity,
- mySystemCallback);
- systemInstall.installPackage(file);
-
- return;
- } catch (Exception e) {
- Log.d(TAG, "Unattended install failed, falling back to normal install method...", e);
- }
-
- Intent intent = new Intent();
- intent.setAction(android.content.Intent.ACTION_VIEW);
- intent.setDataAndType(Uri.parse("file://" + file.getPath()),
- "application/vnd.android.package-archive");
- extraNotUnknownSource(intent);
- mActivity.startActivityForResult(intent, REQUEST_INSTALL);
- }
-
- @TargetApi(14)
- private void extraNotUnknownSource(Intent intent) {
- if (Build.VERSION.SDK_INT < 14) {
- return;
- }
- intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
- }
-
- private InstallSystemManager.InstallSystemCallback mySystemCallback = new InstallSystemManager.InstallSystemCallback() {
-
- @Override
- public void onPackageInstalled(String packageName, int returnCode) {
- if (returnCode == InstallSystemManager.INSTALL_SUCCEEDED) {
- Log.d(TAG, "Install succeeded");
- mInstallCallback.onPackageInstalled(InstallCallback.RETURN_SUCCESS,
- true);
- } else {
- Log.d(TAG, "Install failed: " + returnCode);
- mInstallCallback.onPackageInstalled(InstallCallback.RETURN_CANCEL,
- true);
- }
-
- }
-
- @Override
- public void onPackageDeleted(String packageName, int returnCode) {
- if (returnCode == InstallSystemManager.DELETE_SUCCEEDED) {
- Log.d(TAG, "Delete succeeded");
- mInstallCallback
- .onPackageDeleted(InstallCallback.RETURN_SUCCESS, true);
- } else {
- Log.d(TAG, "Delete failed: " + returnCode);
- mInstallCallback.onPackageDeleted(InstallCallback.RETURN_CANCEL, true);
- }
-
- }
-
- };
-
- protected void handleOnActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case REQUEST_INSTALL:
- if (resultCode == Activity.RESULT_OK) {
- mInstallCallback.onPackageInstalled(InstallCallback.RETURN_SUCCESS, false);
- } else {
- mInstallCallback.onPackageInstalled(InstallCallback.RETURN_CANCEL, false);
- }
- break;
- case REQUEST_UNINSTALL:
- if (resultCode == Activity.RESULT_OK) {
- mInstallCallback.onPackageDeleted(InstallCallback.RETURN_SUCCESS, false);
- } else {
- mInstallCallback.onPackageDeleted(InstallCallback.RETURN_CANCEL, false);
- }
- break;
- }
- }
-
-}
diff --git a/src/org/fdroid/fdroid/InstallSystemManager.java b/src/org/fdroid/fdroid/InstallSystemManager.java
deleted file mode 100644
index ae9da00eb..000000000
--- a/src/org/fdroid/fdroid/InstallSystemManager.java
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 3
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
- */
-
-package org.fdroid.fdroid;
-
-import java.io.File;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import android.content.Context;
-import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.IPackageInstallObserver;
-import android.content.pm.PackageManager;
-import android.net.Uri;
-import android.os.RemoteException;
-
-/**
- * Most parts of this class are based on
- * https://github.com/paulononaka/Android-InstallInBackgroundSample and insights while reading the
- * Android sourcecode.
- *
- * The author granted to use it "for whatever you want"
- * (http://paulononaka.wordpress.com/2011/07/02/
- * how-to-install-a-application-in-background-on-android/#comment-80)
- *
- * Return values copied from PackageManger.java from Android sourcecode
- */
-public class InstallSystemManager {
-
- public interface InstallSystemCallback {
-
- public void onPackageInstalled(String packageName, int returnCode);
-
- public void onPackageDeleted(String packageName, int returnCode);
- }
-
- public final int INSTALL_REPLACE_EXISTING = 2;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} on success.
- *
- * @hide
- */
- public static final int INSTALL_SUCCEEDED = 1;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the package is
- * already installed.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_ALREADY_EXISTS = -1;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the package archive
- * file is invalid.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_INVALID_APK = -2;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the URI passed in
- * is invalid.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_INVALID_URI = -3;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the package manager
- * service found that the device didn't have enough storage space to install the app.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if a package is
- * already installed with the same name.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the requested
- * shared user does not exist.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_NO_SHARED_USER = -6;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if a previously
- * installed package of the same name has a different signature than the new package (and the
- * old package's data was not removed).
- *
- * @hide
- */
- public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the new package is
- * requested a shared user which is already installed on the device and does not have matching
- * signature.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the new package
- * uses a shared library that is not available.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the new package
- * uses a shared library that is not available.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the new package
- * failed while optimizing and validating its dex files, either because there was not enough
- * storage or the validation failed.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_DEXOPT = -11;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the new package
- * failed because the current SDK version is older than that required by the package.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_OLDER_SDK = -12;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the new package
- * failed because it contains a content provider with the same authority as a provider already
- * installed in the system.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the new package
- * failed because the current SDK version is newer than that required by the package.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_NEWER_SDK = -14;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the new package
- * failed because it has specified that it is a test-only package and the caller has not
- * supplied the {@link #INSTALL_ALLOW_TEST} flag.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_TEST_ONLY = -15;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the package being
- * installed contains native code, but none that is compatible with the the device's CPU_ABI.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the new package
- * uses a feature that is not available.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_MISSING_FEATURE = -17;
-
- // ------ Errors related to sdcard
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if a secure container
- * mount point couldn't be accessed on external media.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the new package
- * couldn't be installed in the specified install location.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
-
- /**
- * Installation return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the new package
- * couldn't be installed in the specified install location because the media is not available.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;
-
- /**
- * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the parser was
- * given a path that is not a file, or does not end with the expected '.apk' extension.
- *
- * @hide
- */
- public static final int INSTALL_PARSE_FAILED_NOT_APK = -100;
-
- /**
- * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the parser was
- * unable to retrieve the AndroidManifest.xml file.
- *
- * @hide
- */
- public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101;
-
- /**
- * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the parser
- * encountered an unexpected exception.
- *
- * @hide
- */
- public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102;
-
- /**
- * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the parser did not
- * find any certificates in the .apk.
- *
- * @hide
- */
- public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103;
-
- /**
- * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the parser found
- * inconsistent certificates on the files in the .apk.
- *
- * @hide
- */
- public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104;
-
- /**
- * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the parser
- * encountered a CertificateEncodingException in one of the files in the .apk.
- *
- * @hide
- */
- public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105;
-
- /**
- * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the parser
- * encountered a bad or missing package name in the manifest.
- *
- * @hide
- */
- public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106;
-
- /**
- * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the parser
- * encountered a bad shared user id name in the manifest.
- *
- * @hide
- */
- public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107;
-
- /**
- * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the parser
- * encountered some structural problem in the manifest.
- *
- * @hide
- */
- public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108;
-
- /**
- * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the parser did not
- * find any actionable tags (instrumentation or application) in the manifest.
- *
- * @hide
- */
- public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;
-
- /**
- * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by
- * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the system failed
- * to install the package because of system issues.
- *
- * @hide
- */
- public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;
-
- /**
- * Return code for when package deletion succeeds. This is passed to the
- * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system succeeded in
- * deleting the package.
- *
- * @hide
- */
- public static final int DELETE_SUCCEEDED = 1;
-
- /**
- * Deletion failed return code: this is passed to the {@link IPackageDeleteObserver} by
- * {@link #deletePackage()} if the system failed to delete the package for an unspecified
- * reason.
- *
- * @hide
- */
- public static final int DELETE_FAILED_INTERNAL_ERROR = -1;
-
- /**
- * Deletion failed return code: this is passed to the {@link IPackageDeleteObserver} by
- * {@link #deletePackage()} if the system failed to delete the package because it is the active
- * DevicePolicy manager.
- *
- * @hide
- */
- public static final int DELETE_FAILED_DEVICE_POLICY_MANAGER = -2;
-
- /**
- * Deletion failed return code: this is passed to the {@link IPackageDeleteObserver} by
- * {@link #deletePackage()} if the system failed to delete the package since the user is
- * restricted.
- *
- * @hide
- */
- public static final int DELETE_FAILED_USER_RESTRICTED = -3;
-
- private PackageInstallObserver observer;
- private PackageDeleteObserver observerdelete;
- private PackageManager pm;
- private Method method;
- private Method uninstallmethod;
-
- private InstallSystemCallback installSystemCallback;
-
- class PackageInstallObserver extends IPackageInstallObserver.Stub {
- public void packageInstalled(String packageName, int returnCode) throws RemoteException {
- if (installSystemCallback != null) {
- installSystemCallback.onPackageInstalled(packageName, returnCode);
- }
- }
- }
-
- class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
- public void packageDeleted(String packageName, int returnCode) throws RemoteException {
- if (installSystemCallback != null) {
- installSystemCallback.onPackageDeleted(packageName, returnCode);
- }
- }
- }
-
- public InstallSystemManager(Context context, InstallSystemCallback installCallback) throws SecurityException, NoSuchMethodException {
- observer = new PackageInstallObserver();
- observerdelete = new PackageDeleteObserver();
- pm = context.getPackageManager();
-
- Class>[] types = new Class[] { Uri.class, IPackageInstallObserver.class, int.class,
- String.class };
- Class>[] uninstalltypes = new Class[] { String.class, IPackageDeleteObserver.class,
- int.class };
-
- method = pm.getClass().getMethod("installPackage", types);
- uninstallmethod = pm.getClass().getMethod("deletePackage", uninstalltypes);
-
- this.installSystemCallback = installCallback;
- }
-
- public void deletePackage(String packagename) throws IllegalArgumentException,
- IllegalAccessException, InvocationTargetException {
- uninstallmethod.invoke(pm, new Object[] { packagename, observerdelete, 0 });
- }
-
- public void installPackage(File apkFile) throws IllegalArgumentException,
- IllegalAccessException, InvocationTargetException {
- if (!apkFile.exists())
- throw new IllegalArgumentException();
- Uri packageURI = Uri.fromFile(apkFile);
- installPackage(packageURI);
- }
-
- public void installPackage(Uri apkFile) throws IllegalArgumentException,
- IllegalAccessException, InvocationTargetException {
- method.invoke(pm, new Object[] { apkFile, observer, INSTALL_REPLACE_EXISTING, null });
- }
-
-}
diff --git a/src/org/fdroid/fdroid/installer/DefaultInstaller.java b/src/org/fdroid/fdroid/installer/DefaultInstaller.java
new file mode 100644
index 000000000..9ce5ba9bd
--- /dev/null
+++ b/src/org/fdroid/fdroid/installer/DefaultInstaller.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+package org.fdroid.fdroid.installer;
+
+import java.io.File;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.os.Build;
+
+/**
+ * Default Installer using the public PackageManager API of Android to
+ * install/delete packages. This starts a Activity from the Android OS showing
+ * all permissions/changed permissions. The the user needs to manually press an
+ * install button, this Installer cannot be used for unattended installations.
+ */
+public class DefaultInstaller extends Installer {
+ private Activity mActivity;
+
+ public DefaultInstaller(Activity activity, PackageManager pm, InstallerCallback callback)
+ throws AndroidNotCompatibleException {
+ super(activity, pm, callback);
+ this.mActivity = activity;
+ }
+
+ private static final int REQUEST_CODE_INSTALL = 0;
+ private static final int REQUEST_CODE_DELETE = 1;
+
+ @Override
+ public void installPackage(File file) throws AndroidNotCompatibleException {
+ super.installPackage(file);
+
+ Intent intent = new Intent();
+ intent.setAction(android.content.Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.parse("file://" + file.getPath()),
+ "application/vnd.android.package-archive");
+ extraNotUnknownSource(intent);
+ try {
+ mActivity.startActivityForResult(intent, REQUEST_CODE_INSTALL);
+ } catch (ActivityNotFoundException e) {
+ throw new AndroidNotCompatibleException(e);
+ }
+ }
+
+ @TargetApi(14)
+ private void extraNotUnknownSource(Intent intent) {
+ if (Build.VERSION.SDK_INT < 14) {
+ return;
+ }
+ intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
+ }
+
+ @Override
+ public void deletePackage(String packageName) throws AndroidNotCompatibleException {
+ super.deletePackage(packageName);
+
+ PackageInfo pkgInfo = null;
+ try {
+ pkgInfo = mPm.getPackageInfo(packageName, 0);
+ } catch (NameNotFoundException e) {
+ // already checked in super class
+ }
+
+ Uri uri = Uri.fromParts("package", pkgInfo.packageName, null);
+ Intent intent = new Intent(Intent.ACTION_DELETE, uri);
+ try {
+ mActivity.startActivityForResult(intent, REQUEST_CODE_DELETE);
+ } catch (ActivityNotFoundException e) {
+ throw new AndroidNotCompatibleException(e);
+ }
+ }
+
+ @Override
+ public boolean handleOnActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_CODE_INSTALL:
+ if (resultCode == Activity.RESULT_OK) {
+ mCallback.onPackageInstalled(InstallerCallback.RETURN_SUCCESS, false);
+ } else {
+ mCallback.onPackageInstalled(InstallerCallback.RETURN_CANCEL, false);
+ }
+
+ return true;
+ case REQUEST_CODE_DELETE:
+ if (resultCode == Activity.RESULT_OK) {
+ mCallback.onPackageDeleted(InstallerCallback.RETURN_SUCCESS, false);
+ } else {
+ mCallback.onPackageDeleted(InstallerCallback.RETURN_CANCEL, false);
+ }
+
+ return true;
+ default:
+ return false;
+ }
+ }
+
+}
diff --git a/src/org/fdroid/fdroid/installer/Installer.java b/src/org/fdroid/fdroid/installer/Installer.java
new file mode 100644
index 000000000..2ee8ce1b2
--- /dev/null
+++ b/src/org/fdroid/fdroid/installer/Installer.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+package org.fdroid.fdroid.installer;
+
+import java.io.File;
+
+import android.Manifest.permission;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.Log;
+
+abstract public class Installer {
+ protected Context mContext;
+ protected PackageManager mPm;
+ protected InstallerCallback mCallback;
+
+ public static final String TAG = "FDroid";
+
+ /**
+ * This is thrown when an Installer is not compatible with the Android OS it
+ * is running on. This could be due to a broken superuser in case of
+ * RootInstaller or due to an incompatible Android version in case of
+ * SystemPermissionInstaller
+ */
+ public static class AndroidNotCompatibleException extends Exception {
+
+ private static final long serialVersionUID = -8343133906463328027L;
+
+ public AndroidNotCompatibleException() {
+ }
+
+ public AndroidNotCompatibleException(String message) {
+ super(message);
+ }
+
+ public AndroidNotCompatibleException(Throwable cause) {
+ super(cause);
+ }
+
+ public AndroidNotCompatibleException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+ public interface InstallerCallback {
+
+ public static final int RETURN_SUCCESS = 1;
+ public static final int RETURN_CANCEL = 0;
+
+ public void onPackageInstalled(int returnCode, boolean unattended);
+
+ public void onPackageDeleted(int returnCode, boolean unattended);
+ }
+
+ public Installer(Context context, PackageManager pm, InstallerCallback callback)
+ throws AndroidNotCompatibleException {
+ this.mContext = context;
+ this.mPm = pm;
+ this.mCallback = callback;
+ }
+
+ /**
+ * Creates a new Installer for installing/deleting processes starting from
+ * an Activity
+ *
+ * @param context
+ * @param pm
+ * @param callback
+ * @return
+ * @throws AndroidNotCompatibleException
+ */
+ public static Installer getActivityInstaller(Activity activity, PackageManager pm,
+ InstallerCallback callback) {
+
+ // system permissions -> SystemPermissionInstaller
+ if (hasSystemPermissions(activity, pm)) {
+ Log.d(TAG, "system permissions -> SystemPermissionInstaller");
+
+ try {
+ return new SystemPermissionInstaller(activity, pm, callback);
+ } catch (AndroidNotCompatibleException e) {
+ Log.e(TAG, "Android not compatible with SystemPermissionInstaller!", e);
+ }
+ }
+
+ // try default installer
+ try {
+ Log.d(TAG, "try default installer");
+
+ return new DefaultInstaller(activity, pm, callback);
+ } catch (AndroidNotCompatibleException e) {
+ Log.e(TAG,
+ "Android not compatible with DefaultInstaller! This should really not happen!",
+ e);
+ }
+
+ // this should not happen!
+ return null;
+ }
+
+ public static Installer getUnattendedInstaller(Context context, PackageManager pm,
+ InstallerCallback callback) throws AndroidNotCompatibleException {
+
+ if (hasSystemPermissions(context, pm)) {
+ // we have system permissions!
+ return new SystemPermissionInstaller(context, pm, callback);
+ } else {
+ // nope!
+ throw new AndroidNotCompatibleException();
+ }
+ }
+
+ private static boolean hasSystemPermissions(Context context, PackageManager pm) {
+ int checkInstallPermission =
+ pm.checkPermission(permission.INSTALL_PACKAGES, context.getPackageName());
+ int checkDeletePermission =
+ pm.checkPermission(permission.DELETE_PACKAGES, context.getPackageName());
+ boolean permissionsGranted = (checkInstallPermission == PackageManager.PERMISSION_GRANTED
+ && checkDeletePermission == PackageManager.PERMISSION_GRANTED);
+
+ boolean isSystemApp;
+ try {
+ isSystemApp = isSystemApp(pm.getApplicationInfo(context.getPackageName(), 0));
+ } catch (NameNotFoundException e) {
+ isSystemApp = false;
+ }
+
+ // TODO: is this right???
+ // two ways to be able to get system permissions: somehow the
+ // permissions where actually granted on install or the app has been
+ // moved later to the system partition -> also access
+ if (permissionsGranted || isSystemApp) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private static boolean isSystemApp(ApplicationInfo ai) {
+ int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+ return (ai.flags & mask) != 0;
+ }
+
+ public void installPackage(File apkFile) throws AndroidNotCompatibleException {
+ // check if file exists...
+ if (!apkFile.exists()) {
+ Log.d(TAG, "Couldn't find file " + apkFile + " to install.");
+ return;
+ }
+
+ // extended class now actually installs the package
+ }
+
+ public void deletePackage(String packageName) throws AndroidNotCompatibleException {
+ // check if package exists before proceeding...
+ try {
+ mPm.getPackageInfo(packageName, 0);
+ } catch (NameNotFoundException e) {
+ Log.d(TAG, "Couldn't find package " + packageName + " to delete.");
+ return;
+ }
+
+ // extended class now actually deletes the package
+ }
+
+ public abstract boolean handleOnActivityResult(int requestCode, int resultCode, Intent data);
+}
diff --git a/src/org/fdroid/fdroid/installer/SystemPermissionInstaller.java b/src/org/fdroid/fdroid/installer/SystemPermissionInstaller.java
new file mode 100644
index 000000000..207cb57b9
--- /dev/null
+++ b/src/org/fdroid/fdroid/installer/SystemPermissionInstaller.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+package org.fdroid.fdroid.installer;
+
+import java.io.File;
+import java.lang.reflect.Method;
+
+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.RemoteException;
+import android.util.Log;
+
+/**
+ * Installer based on using internal hidden APIs of the Android OS, which are
+ * protected by the permissions
+ *
+ * - android.permission.INSTALL_PACKAGES
+ * - android.permission.DELETE_PACKAGES
+ *
+ *
+ * Both permissions are only granted on F-Droid's install when F-Droid itself
+ * has been installed as a system-application.
+ */
+public class SystemPermissionInstaller extends Installer {
+
+ private PackageInstallObserver mInstallObserver;
+ private PackageDeleteObserver mDeleteObserver;
+ private Method mInstallMethod;
+ private Method mDeleteMethod;
+
+ public SystemPermissionInstaller(Context context, PackageManager pm,
+ InstallerCallback callback) throws AndroidNotCompatibleException {
+ super(context, pm, callback);
+
+ // create internal callbacks
+ mInstallObserver = new PackageInstallObserver();
+ mDeleteObserver = new PackageDeleteObserver();
+
+ try {
+ Class>[] installTypes = new Class[] {
+ Uri.class, IPackageInstallObserver.class, int.class,
+ String.class
+ };
+ Class>[] deleteTypes = new Class[] {
+ 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);
+ }
+ }
+
+ /**
+ * Internal install callback from the system
+ */
+ class PackageInstallObserver extends IPackageInstallObserver.Stub {
+ public void packageInstalled(String packageName, int returnCode) throws RemoteException {
+ // TODO: propagate other return codes?
+ if (returnCode == INSTALL_SUCCEEDED) {
+ Log.d(TAG, "Install succeeded");
+ mCallback.onPackageInstalled(InstallerCallback.RETURN_SUCCESS, true);
+ } else {
+ Log.d(TAG, "Install failed: " + returnCode);
+ mCallback.onPackageInstalled(InstallerCallback.RETURN_CANCEL, true);
+ }
+ }
+ }
+
+ /**
+ * 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) {
+ Log.d(TAG, "Delete succeeded");
+ mCallback.onPackageDeleted(InstallerCallback.RETURN_SUCCESS, true);
+ } else {
+ Log.d(TAG, "Delete failed: " + returnCode);
+ mCallback.onPackageDeleted(InstallerCallback.RETURN_CANCEL, true);
+ }
+ }
+ }
+
+ @Override
+ public void installPackage(File apkFile) throws AndroidNotCompatibleException {
+ super.installPackage(apkFile);
+
+ Uri packageURI = Uri.fromFile(apkFile);
+ try {
+ mInstallMethod.invoke(mPm, new Object[] {
+ packageURI, mInstallObserver, INSTALL_REPLACE_EXISTING, null
+ });
+ } catch (Exception e) {
+ throw new AndroidNotCompatibleException(e);
+ }
+ }
+
+ @Override
+ public void deletePackage(String packageName) throws AndroidNotCompatibleException {
+ super.deletePackage(packageName);
+
+ try {
+ mDeleteMethod.invoke(mPm, new Object[] {
+ packageName, mDeleteObserver, 0
+ });
+ } catch (Exception e) {
+ throw new AndroidNotCompatibleException(e);
+ }
+ }
+
+ @Override
+ public boolean handleOnActivityResult(int requestCode, int resultCode, Intent data) {
+ // no need to handle onActivityResult
+ return false;
+ }
+
+ public final int INSTALL_REPLACE_EXISTING = 2;
+
+ /**
+ * Following return codes are copied from Android 4.3 source code
+ */
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} on
+ * success.
+ */
+ public static final int INSTALL_SUCCEEDED = 1;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the package is already installed.
+ */
+ public static final int INSTALL_FAILED_ALREADY_EXISTS = -1;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the package archive file is invalid.
+ */
+ public static final int INSTALL_FAILED_INVALID_APK = -2;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the URI passed in is invalid.
+ */
+ public static final int INSTALL_FAILED_INVALID_URI = -3;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the package manager service found that the device didn't have enough
+ * storage space to install the app.
+ */
+ public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * a package is already installed with the same name.
+ */
+ public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the requested shared user does not exist.
+ */
+ public static final int INSTALL_FAILED_NO_SHARED_USER = -6;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * a previously installed package of the same name has a different signature
+ * than the new package (and the old package's data was not removed).
+ */
+ public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package is requested a shared user which is already installed on
+ * the device and does not have matching signature.
+ */
+ public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package uses a shared library that is not available.
+ */
+ public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package uses a shared library that is not available.
+ */
+ public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package failed while optimizing and validating its dex files,
+ * either because there was not enough storage or the validation failed.
+ */
+ public static final int INSTALL_FAILED_DEXOPT = -11;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package failed because the current SDK version is older than that
+ * required by the package.
+ */
+ public static final int INSTALL_FAILED_OLDER_SDK = -12;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package failed because it contains a content provider with the
+ * same authority as a provider already installed in the system.
+ */
+ public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package failed because the current SDK version is newer than that
+ * required by the package.
+ */
+ public static final int INSTALL_FAILED_NEWER_SDK = -14;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package failed because it has specified that it is a test-only
+ * package and the caller has not supplied the {@link #INSTALL_ALLOW_TEST}
+ * flag.
+ */
+ public static final int INSTALL_FAILED_TEST_ONLY = -15;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the package being installed contains native code, but none that is
+ * compatible with the the device's CPU_ABI.
+ */
+ public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package uses a feature that is not available.
+ */
+ public static final int INSTALL_FAILED_MISSING_FEATURE = -17;
+
+ // ------ Errors related to sdcard
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * a secure container mount point couldn't be accessed on external media.
+ */
+ public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package couldn't be installed in the specified install location.
+ */
+ public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
+
+ /**
+ * Installation return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package couldn't be installed in the specified install location
+ * because the media is not available.
+ */
+ public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;
+
+ /**
+ * Installation parse return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the parser was given a path that is not a file, or does not end with the
+ * expected '.apk' extension.
+ */
+ public static final int INSTALL_PARSE_FAILED_NOT_APK = -100;
+
+ /**
+ * Installation parse return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the parser was unable to retrieve the AndroidManifest.xml file.
+ */
+ public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101;
+
+ /**
+ * Installation parse return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the parser encountered an unexpected exception.
+ */
+ public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102;
+
+ /**
+ * Installation parse return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the parser did not find any certificates in the .apk.
+ */
+ public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103;
+
+ /**
+ * Installation parse return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the parser found inconsistent certificates on the files in the .apk.
+ */
+ public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104;
+
+ /**
+ * Installation parse return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the parser encountered a CertificateEncodingException in one of the files
+ * in the .apk.
+ */
+ public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105;
+
+ /**
+ * Installation parse return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the parser encountered a bad or missing package name in the manifest.
+ */
+ public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106;
+
+ /**
+ * Installation parse return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the parser encountered a bad shared user id name in the manifest.
+ */
+ public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107;
+
+ /**
+ * Installation parse return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the parser encountered some structural problem in the manifest.
+ */
+ public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108;
+
+ /**
+ * Installation parse return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the parser did not find any actionable tags (instrumentation or
+ * application) in the manifest.
+ */
+ public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;
+
+ /**
+ * Installation failed return code: this is passed to the
+ * {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the system failed to install the package because of system issues.
+ */
+ public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;
+
+ /**
+ * Return code for when package deletion succeeds. This is passed to the
+ * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
+ * succeeded in deleting the package.
+ */
+ public static final int DELETE_SUCCEEDED = 1;
+
+ /**
+ * Deletion failed return code: this is passed to the
+ * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
+ * failed to delete the package for an unspecified reason.
+ */
+ public static final int DELETE_FAILED_INTERNAL_ERROR = -1;
+
+ /**
+ * Deletion failed return code: this is passed to the
+ * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
+ * failed to delete the package because it is the active DevicePolicy
+ * manager.
+ */
+ public static final int DELETE_FAILED_DEVICE_POLICY_MANAGER = -2;
+
+ /**
+ * Deletion failed return code: this is passed to the
+ * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
+ * failed to delete the package since the user is restricted.
+ */
+ public static final int DELETE_FAILED_USER_RESTRICTED = -3;
+}