Infrastructure to support package verifier
Allow a package verifier to approve or disapprove of a package being
installed.
Change-Id: Ibfea0f2b1aaa4ab1589a4e59f96144702b9bf94b
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index c980715..0ec007c 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -772,18 +772,33 @@
}
}
- String apkFilePath = nextArg();
+ final Uri apkURI;
+ final Uri verificationURI;
+
+ // Populate apkURI, must be present
+ final String apkFilePath = nextArg();
System.err.println("\tpkg: " + apkFilePath);
- if (apkFilePath == null) {
+ if (apkFilePath != null) {
+ apkURI = Uri.fromFile(new File(apkFilePath));
+ } else {
System.err.println("Error: no package specified");
showUsage();
return;
}
+ // Populate verificationURI, optionally present
+ final String verificationFilePath = nextArg();
+ if (verificationFilePath != null) {
+ System.err.println("\tver: " + verificationFilePath);
+ verificationURI = Uri.fromFile(new File(verificationFilePath));
+ } else {
+ verificationURI = null;
+ }
+
PackageInstallObserver obs = new PackageInstallObserver();
try {
- mPm.installPackage(Uri.fromFile(new File(apkFilePath)), obs, installFlags,
- installerPackageName);
+ mPm.installPackageWithVerification(apkURI, obs, installFlags, installerPackageName,
+ verificationURI, null);
synchronized (obs) {
while (!obs.finished) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 4cff12f..4b2a8d2 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -41,11 +41,11 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
+import android.content.pm.ManifestDigest;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
@@ -941,6 +941,27 @@
}
@Override
+ public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
+ int flags, String installerPackageName, Uri verificationURI,
+ ManifestDigest manifestDigest) {
+ try {
+ mPM.installPackageWithVerification(packageURI, observer, flags, installerPackageName,
+ verificationURI, manifestDigest);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
+ public void verifyPendingInstall(int id, boolean verified, String failureMessage) {
+ try {
+ mPM.verifyPendingInstall(id, verified, failureMessage);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
public void setInstallerPackageName(String targetPackage,
String installerPackageName) {
try {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2579ced..8d6cee1 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1530,6 +1530,18 @@
public static final String ACTION_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH";
/**
+ * Broadcast Action: Sent to the system package verifier when a package
+ * needs to be verified. The data contains the package URI.
+ * <p class="note">
+ * This is a protected intent that can only be sent by the system.
+ * </p>
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PACKAGE_NEEDS_VERIFICATION = "android.intent.action.PACKAGE_NEEDS_VERIFICATION";
+
+ /**
* Broadcast Action: Resources for a set of packages (which were
* previously unavailable) are currently
* available since the media on which they exist is available.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 37b6822..d7607e3 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -30,6 +30,7 @@
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.ManifestDigest;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.PermissionGroupInfo;
@@ -346,4 +347,10 @@
UserInfo createUser(in String name, int flags);
boolean removeUser(int userId);
+
+ void installPackageWithVerification(in Uri packageURI, in IPackageInstallObserver observer,
+ int flags, in String installerPackageName, in Uri verificationURI,
+ in ManifestDigest manifestDigest);
+
+ void verifyPendingInstall(int id, boolean verified, in String message);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index dd684cd..5c641f1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.pm.ManifestDigest;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
@@ -289,11 +290,19 @@
public static final int INSTALL_EXTERNAL = 0x00000008;
/**
- * Flag parameter for {@link #installPackage} to indicate that this
- * package has to be installed on the sdcard.
- * @hide
- */
- public static final int INSTALL_INTERNAL = 0x00000010;
+ * Flag parameter for {@link #installPackage} to indicate that this package
+ * has to be installed on the sdcard.
+ * @hide
+ */
+ public static final int INSTALL_INTERNAL = 0x00000010;
+
+ /**
+ * Flag parameter for {@link #installPackage} to indicate that this install
+ * was initiated via ADB.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FROM_ADB = 0x00000020;
/**
* Flag parameter for
@@ -483,6 +492,30 @@
public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;
/**
+ * 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 because the verification timed out.
+ * @hide
+ */
+ public static final int INSTALL_FAILED_VERIFICATION_TIMEOUT = -21;
+
+ /**
+ * 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 because the verification did not succeed.
+ * @hide
+ */
+ public static final int INSTALL_FAILED_VERIFICATION_FAILURE = -22;
+
+ /**
+ * Installation return code: this is passed to the {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the package changed from what the calling program expected.
+ * @hide
+ */
+ public static final int INSTALL_FAILED_PACKAGE_CHANGED = -23;
+
+ /**
* 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
@@ -995,35 +1028,63 @@
= "android.content.pm.CLEAN_EXTERNAL_STORAGE";
/**
+ * Extra field name for the URI to a verification file. Passed to a package
+ * verifier.
+ *
+ * @hide
+ */
+ public static final String EXTRA_VERIFICATION_URI = "android.content.pm.extra.VERIFICATION_URI";
+
+ /**
+ * Extra field name for the ID of a package pending verification. Passed to
+ * a package verifier and is used to call back to
+ * {@link PackageManager#verifyPendingInstall(int, boolean)}
+ *
+ * @hide
+ */
+ public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
+
+ /**
+ * Extra field name for the package identifier which is trying to install
+ * the package.
+ *
+ * @hide
+ */
+ public static final String EXTRA_VERIFICATION_INSTALLER_PACKAGE
+ = "android.content.pm.extra.VERIFICATION_INSTALLER_PACKAGE";
+
+ /**
+ * Extra field name for the requested install flags for a package pending
+ * verification. Passed to a package verifier.
+ *
+ * @hide
+ */
+ public static final String EXTRA_VERIFICATION_INSTALL_FLAGS
+ = "android.content.pm.extra.VERIFICATION_INSTALL_FLAGS";
+
+ /**
* Retrieve overall information about an application package that is
* installed on the system.
- *
- * <p>Throws {@link NameNotFoundException} if a package with the given
- * name can not be found on the system.
+ * <p>
+ * Throws {@link NameNotFoundException} if a package with the given name can
+ * not be found on the system.
*
* @param packageName The full name (i.e. com.google.apps.contacts) of the
- * desired package.
-
+ * desired package.
* @param flags Additional option flags. Use any combination of
- * {@link #GET_ACTIVITIES},
- * {@link #GET_GIDS},
- * {@link #GET_CONFIGURATIONS},
- * {@link #GET_INSTRUMENTATION},
- * {@link #GET_PERMISSIONS},
- * {@link #GET_PROVIDERS},
- * {@link #GET_RECEIVERS},
- * {@link #GET_SERVICES},
- * {@link #GET_SIGNATURES},
- * {@link #GET_UNINSTALLED_PACKAGES} to modify the data returned.
- *
- * @return Returns a PackageInfo object containing information about the package.
- * If flag GET_UNINSTALLED_PACKAGES is set and if the package is not
- * found in the list of installed applications, the package information is
- * retrieved from the list of uninstalled applications(which includes
- * installed applications as well as applications
- * with data directory ie applications which had been
+ * {@link #GET_ACTIVITIES}, {@link #GET_GIDS},
+ * {@link #GET_CONFIGURATIONS}, {@link #GET_INSTRUMENTATION},
+ * {@link #GET_PERMISSIONS}, {@link #GET_PROVIDERS},
+ * {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
+ * {@link #GET_SIGNATURES}, {@link #GET_UNINSTALLED_PACKAGES} to
+ * modify the data returned.
+ * @return Returns a PackageInfo object containing information about the
+ * package. If flag GET_UNINSTALLED_PACKAGES is set and if the
+ * package is not found in the list of installed applications, the
+ * package information is retrieved from the list of uninstalled
+ * applications(which includes installed applications as well as
+ * applications with data directory ie applications which had been
* deleted with DONT_DELTE_DATA flag set).
- *
* @see #GET_ACTIVITIES
* @see #GET_GIDS
* @see #GET_CONFIGURATIONS
@@ -1034,7 +1095,6 @@
* @see #GET_SERVICES
* @see #GET_SIGNATURES
* @see #GET_UNINSTALLED_PACKAGES
- *
*/
public abstract PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException;
@@ -2061,6 +2121,46 @@
String installerPackageName);
/**
+ * Similar to
+ * {@link #installPackage(Uri, IPackageInstallObserver, int, String)} but
+ * with an extra verification file provided.
+ *
+ * @param packageURI The location of the package file to install. This can
+ * be a 'file:' or a 'content:' URI.
+ * @param observer An observer callback to get notified when the package
+ * installation is complete.
+ * {@link IPackageInstallObserver#packageInstalled(String, int)}
+ * will be called when that happens. observer may be null to
+ * indicate that no callback is desired.
+ * @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 verificationURI The location of the supplementary verification
+ * file. This can be a 'file:' or a 'content:' URI.
+ * @hide
+ */
+ public abstract void installPackageWithVerification(Uri packageURI,
+ IPackageInstallObserver observer, int flags, String installerPackageName,
+ Uri verificationURI, ManifestDigest manifestDigest);
+
+ /**
+ * Allows a package listening to the
+ * {@link Intent#ACTION_PACKAGE_NEEDS_VERIFICATION package verification
+ * broadcast} to respond to the package manager.
+ *
+ * @param id pending package identifier as passed via the
+ * {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra
+ * @param verified whether the package was verified as valid
+ * @param failureMessage if verification was false, this is the error
+ * message that may be shown to the user
+ * @hide
+ */
+ public abstract void verifyPendingInstall(int id, boolean verified, String failureMessage);
+
+ /**
* Change the installer associated with a given package. There are limitations
* on how the installer package can be changed; in particular:
* <ul>
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f8702b9..de06f20 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3960,6 +3960,12 @@
public static final String WEB_AUTOFILL_QUERY_URL =
"web_autofill_query_url";
+ /** Whether package verification is enabled. {@hide} */
+ public static final String PACKAGE_VERIFIER_ENABLE = "verifier_enable";
+
+ /** Timeout for package verification. {@hide} */
+ public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout";
+
/**
* @hide
*/
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b9868db..2dbb0b2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -42,6 +42,7 @@
<protected-broadcast android:name="android.intent.action.PACKAGE_RESTARTED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_FIRST_LAUNCH" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_NEEDS_VERIFICATION" />
<protected-broadcast android:name="android.intent.action.UID_REMOVED" />
<protected-broadcast android:name="android.intent.action.CONFIGURATION_CHANGED" />
<protected-broadcast android:name="android.intent.action.LOCALE_CHANGED" />
@@ -1429,6 +1430,24 @@
android:protectionLevel="signature" />
<uses-permission android:name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"/>
+ <!-- Package verifier needs to have this permission before the PackageManager will
+ trust it to verify packages.
+ @hide
+ -->
+ <permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT"
+ android:label="@string/permlab_packageVerificationAgent"
+ android:description="@string/permdesc_packageVerificationAgent"
+ android:protectionLevel="signatureOrSystem" />
+
+ <!-- Must be required by package verifier receiver, to ensure that only the
+ system can interact with it.
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_PACKAGE_VERIFIER"
+ android:label="@string/permlab_bindPackageVerifier"
+ android:description="@string/permdesc_bindPackageVerifier"
+ android:protectionLevel="signature" />
+
<!-- The system process is explicitly the only one allowed to launch the
confirmation UI for full backup/restore -->
<uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7d6d25c..c70e3d2 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2181,6 +2181,22 @@
Browser\'s geolocation permissions. Malicious applications
can use this to allow sending location information to arbitrary web sites.</string>
+ <!-- Title of an application permission which allows the application to verify whether
+ a different package is able to be installed by some internal logic. [CHAR LIMIT=40] -->
+ <string name="permlab_packageVerificationAgent">verify packages</string>
+ <!-- Description of an application permission which allows the application to verify whether
+ a different package is able to be installed by some internal heuristic. [CHAR LIMIT=NONE] -->
+ <string name="permdesc_packageVerificationAgent">Allows the application to verify a package is
+ installable.</string>
+
+ <!-- Title of an application permission which allows the application to verify whether
+ a different package is able to be installed by some internal heuristic. [CHAR LIMIT=40] -->
+ <string name="permlab_bindPackageVerifier">bind to a package verifier</string>
+ <!-- Description of an application permission which allows the application to verify whether
+ a different package is able to be installed by some internal heuristic. [CHAR LIMIT=NONE] -->
+ <string name="permdesc_bindPackageVerifier">Allows the holder to make requests of
+ package verifiers. Should never be needed for normal applications.</string>
+
<!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Text in the save password dialog, asking if the browser should remember a password. -->
<string name="save_password_message">Do you want the browser to remember this password?</string>
<!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Button in the save password dialog, saying not to remember this password. -->
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 177cf41..abb62de 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -38,6 +38,7 @@
import android.app.IActivityManager;
import android.app.admin.IDevicePolicyManager;
import android.app.backup.IBackupManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -69,6 +70,7 @@
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
+import android.content.pm.ManifestDigest;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -188,6 +190,17 @@
static final int REMOVE_CHATTY = 1<<16;
+ /**
+ * Whether verification is enabled by default.
+ */
+ private static final boolean DEFAULT_VERIFY_ENABLE = true;
+
+ /**
+ * The default maximum time to wait for the verification agent to return in
+ * milliseconds.
+ */
+ private static final long DEFAULT_VERIFICATION_TIMEOUT = 60 * 1000;
+
static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
@@ -333,6 +346,12 @@
// Broadcast actions that are only available to the system.
final HashSet<String> mProtectedBroadcasts = new HashSet<String>();
+ /** List of packages waiting for verification. */
+ final SparseArray<InstallArgs> mPendingVerification = new SparseArray<InstallArgs>();
+
+ /** Token for keys in mPendingVerification. */
+ private int mPendingVerificationToken = 0;
+
boolean mSystemReady;
boolean mSafeMode;
boolean mHasSystemUidErrors;
@@ -364,6 +383,8 @@
static final int UPDATED_MEDIA_STATUS = 12;
static final int WRITE_SETTINGS = 13;
static final int WRITE_STOPPED_PACKAGES = 14;
+ static final int PACKAGE_VERIFIED = 15;
+ static final int CHECK_PENDING_VERIFICATION = 16;
static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds
@@ -444,10 +465,10 @@
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "init_copy");
+ if (DEBUG_INSTALL) Slog.i(TAG, "init_copy");
HandlerParams params = (HandlerParams) msg.obj;
int idx = mPendingInstalls.size();
- if (DEBUG_SD_INSTALL) Log.i(TAG, "idx=" + idx);
+ if (DEBUG_INSTALL) Slog.i(TAG, "idx=" + idx);
// If a bind was already initiated we dont really
// need to do anything. The pending install
// will be processed later on.
@@ -474,7 +495,7 @@
break;
}
case MCS_BOUND: {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_bound");
+ if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
}
@@ -525,8 +546,8 @@
}
break;
}
- case MCS_RECONNECT : {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_reconnect");
+ case MCS_RECONNECT: {
+ if (DEBUG_INSTALL) Slog.i(TAG, "mcs_reconnect");
if (mPendingInstalls.size() > 0) {
if (mBound) {
disconnectService();
@@ -543,27 +564,31 @@
}
break;
}
- case MCS_UNBIND : {
+ case MCS_UNBIND: {
// If there is no actual work left, then time to unbind.
- if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_unbind");
- if (mPendingInstalls.size() == 0) {
+ if (DEBUG_INSTALL) Slog.i(TAG, "mcs_unbind");
+
+ if (mPendingInstalls.size() == 0 && mPendingVerification.size() == 0) {
if (mBound) {
+ if (DEBUG_INSTALL) Slog.i(TAG, "calling disconnectService()");
+
disconnectService();
}
- } else {
+ } else if (mPendingInstalls.size() > 0) {
// There are more pending requests in queue.
// Just post MCS_BOUND message to trigger processing
// of next pending install.
mHandler.sendEmptyMessage(MCS_BOUND);
}
+
break;
}
case MCS_GIVE_UP: {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_giveup too many retries");
+ if (DEBUG_INSTALL) Slog.i(TAG, "mcs_giveup too many retries");
mPendingInstalls.remove(0);
break;
}
- case SEND_PENDING_BROADCAST : {
+ case SEND_PENDING_BROADCAST: {
String packages[];
ArrayList<String> components[];
int size = 0;
@@ -707,6 +732,52 @@
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
} break;
+ case CHECK_PENDING_VERIFICATION: {
+ final int verificationId = msg.arg1;
+ final InstallArgs args = mPendingVerification.get(verificationId);
+
+ if (args != null) {
+ Slog.i(TAG, "Validation timed out for " + args.packageURI.toString());
+ mPendingVerification.remove(verificationId);
+
+ int ret = PackageManager.INSTALL_FAILED_VERIFICATION_TIMEOUT;
+ processPendingInstall(args, ret);
+
+ mHandler.sendEmptyMessage(MCS_UNBIND);
+ }
+
+ break;
+ }
+ case PACKAGE_VERIFIED: {
+ final int verificationId = msg.arg1;
+ final boolean verified = msg.arg2 == 1 ? true : false;
+
+ final InstallArgs args = mPendingVerification.get(verificationId);
+ if (args == null) {
+ Slog.w(TAG, "Invalid validation token " + verificationId + " received");
+ break;
+ }
+
+ mPendingVerification.remove(verificationId);
+
+ int ret;
+ if (verified) {
+ ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ try {
+ ret = args.copyApk(mContainerService, true);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not contact the ContainerService");
+ }
+ } else {
+ ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
+ }
+
+ processPendingInstall(args, ret);
+
+ mHandler.sendEmptyMessage(MCS_UNBIND);
+
+ break;
+ }
}
}
}
@@ -4693,12 +4764,45 @@
public void installPackage(
final Uri packageURI, final IPackageInstallObserver observer, final int flags,
final String installerPackageName) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INSTALL_PACKAGES, null);
+ installPackageWithVerification(packageURI, observer, flags, installerPackageName, null,
+ null);
+ }
- Message msg = mHandler.obtainMessage(INIT_COPY);
- msg.obj = new InstallParams(packageURI, observer, flags,
- installerPackageName);
+ @Override
+ public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
+ int flags, String installerPackageName, Uri verificationURI,
+ ManifestDigest manifestDigest) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
+
+ final int uid = Binder.getCallingUid();
+
+ final int filteredFlags;
+
+ if (uid == Process.SHELL_UID || uid == 0) {
+ if (DEBUG_INSTALL) {
+ Slog.v(TAG, "Install from ADB");
+ }
+ filteredFlags = flags | PackageManager.INSTALL_FROM_ADB;
+ } else {
+ filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB;
+ }
+
+ final Message msg = mHandler.obtainMessage(INIT_COPY);
+ msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,
+ verificationURI, manifestDigest);
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void verifyPendingInstall(int id, boolean verified, String message)
+ throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, null);
+
+ final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
+ msg.arg1 = id;
+ msg.arg2 = verified ? 1 : 0;
+ msg.obj = message;
mHandler.sendMessage(msg);
}
@@ -4713,6 +4817,28 @@
mHandler.sendMessage(msg);
}
+ /**
+ * Get the verification agent timeout.
+ *
+ * @return verification timeout in milliseconds
+ */
+ private long getVerificationTimeout() {
+ return android.provider.Settings.Secure.getLong(mContext.getContentResolver(),
+ android.provider.Settings.Secure.PACKAGE_VERIFIER_TIMEOUT,
+ DEFAULT_VERIFICATION_TIMEOUT);
+ }
+
+ /**
+ * Check whether or not package verification has been enabled.
+ *
+ * @return true if verification should be performed
+ */
+ private boolean isVerificationEnabled() {
+ return android.provider.Settings.Secure.getInt(mContext.getContentResolver(),
+ android.provider.Settings.Secure.PACKAGE_VERIFIER_ENABLE,
+ DEFAULT_VERIFY_ENABLE ? 1 : 0) == 1 ? true : false;
+ }
+
public void setInstallerPackageName(String targetPackage, String installerPackageName) {
final int uid = Binder.getCallingUid();
// writer
@@ -4856,15 +4982,21 @@
});
}
- abstract class HandlerParams {
- final static int MAX_RETRIES = 4;
- int retry = 0;
+ private abstract class HandlerParams {
+ private static final int MAX_RETRIES = 4;
+
+ /**
+ * Number of times startCopy() has been attempted and had a non-fatal
+ * error.
+ */
+ private int mRetries = 0;
+
final boolean startCopy() {
boolean res;
try {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "startCopy");
- retry++;
- if (retry > MAX_RETRIES) {
+ if (DEBUG_INSTALL) Slog.i(TAG, "startCopy");
+
+ if (++mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
@@ -4874,7 +5006,7 @@
res = true;
}
} catch (RemoteException e) {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting install MCS_RECONNECT");
+ if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
@@ -4883,10 +5015,11 @@
}
final void serviceError() {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "serviceError");
+ if (DEBUG_INSTALL) Slog.i(TAG, "serviceError");
handleServiceError();
handleReturnCode();
}
+
abstract void handleStartCopy() throws RemoteException;
abstract void handleServiceError();
abstract void handleReturnCode();
@@ -4969,15 +5102,20 @@
int flags;
final Uri packageURI;
final String installerPackageName;
+ final Uri verificationURI;
+ final ManifestDigest manifestDigest;
private InstallArgs mArgs;
private int mRet;
+
InstallParams(Uri packageURI,
IPackageInstallObserver observer, int flags,
- String installerPackageName) {
+ String installerPackageName, Uri verificationURI, ManifestDigest manifestDigest) {
this.packageURI = packageURI;
this.flags = flags;
this.observer = observer;
this.installerPackageName = installerPackageName;
+ this.verificationURI = verificationURI;
+ this.manifestDigest = manifestDigest;
}
private int installLocationPolicy(PackageInfoLite pkgLite, int flags) {
@@ -5102,13 +5240,70 @@
}
}
}
- // Create the file args now.
- mArgs = createInstallArgs(this);
+
+ final InstallArgs args = createInstallArgs(this);
if (ret == PackageManager.INSTALL_SUCCEEDED) {
- // Create copy only if we are not in an erroneous state.
- // Remote call to initiate copy using temporary file
- ret = mArgs.copyApk(mContainerService, true);
+ /*
+ * Determine if we have any installed package verifiers. If we
+ * do, then we'll defer to them to verify the packages.
+ */
+ final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION,
+ packageURI);
+ verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ final List<ResolveInfo> receivers = queryIntentReceivers(verification, null,
+ PackageManager.GET_DISABLED_COMPONENTS);
+ if (isVerificationEnabled() && receivers.size() > 0) {
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
+ + verification.toString());
+ }
+
+ final int verificationId = mPendingVerificationToken++;
+
+ verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
+
+ verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE,
+ installerPackageName);
+
+ verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS, flags);
+
+ if (verificationURI != null) {
+ verification.putExtra(PackageManager.EXTRA_VERIFICATION_URI,
+ verificationURI);
+ }
+
+ mPendingVerification.append(verificationId, args);
+
+ /*
+ * Send the intent to the registered verification agents,
+ * but only start the verification timeout after the target
+ * BroadcastReceivers have run.
+ */
+ mContext.sendOrderedBroadcast(verification,
+ android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final Message msg = mHandler
+ .obtainMessage(CHECK_PENDING_VERIFICATION);
+ msg.arg1 = verificationId;
+ mHandler.sendMessageDelayed(msg, getVerificationTimeout());
+ }
+ },
+ null, 0, null, null);
+ } else {
+ // Create copy only if we are not in an erroneous state.
+ // Remote call to initiate copy using temporary file
+ mArgs = args;
+ ret = args.copyApk(mContainerService, true);
+ }
+ } else {
+ // There was an error, so let the processPendingInstall() break
+ // the bad news... uh, through a call in handleReturnCode()
+ mArgs = args;
}
+
mRet = ret;
}
@@ -5233,14 +5428,15 @@
final int flags;
final Uri packageURI;
final String installerPackageName;
+ final ManifestDigest manifestDigest;
- InstallArgs(Uri packageURI,
- IPackageInstallObserver observer, int flags,
- String installerPackageName) {
+ InstallArgs(Uri packageURI, IPackageInstallObserver observer, int flags,
+ String installerPackageName, ManifestDigest manifestDigest) {
this.packageURI = packageURI;
this.flags = flags;
this.observer = observer;
this.installerPackageName = installerPackageName;
+ this.manifestDigest = manifestDigest;
}
abstract void createCopyFile();
@@ -5265,12 +5461,12 @@
boolean created = false;
FileInstallArgs(InstallParams params) {
- super(params.packageURI, params.observer,
- params.flags, params.installerPackageName);
+ super(params.packageURI, params.observer, params.flags, params.installerPackageName,
+ params.manifestDigest);
}
FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) {
- super(null, null, 0, null);
+ super(null, null, 0, null, null);
File codeFile = new File(fullCodePath);
installDir = codeFile.getParentFile();
codeFileName = fullCodePath;
@@ -5279,7 +5475,7 @@
}
FileInstallArgs(Uri packageURI, String pkgName, String dataDir) {
- super(packageURI, null, 0, null);
+ super(packageURI, null, 0, null, null);
installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
String apkName = getNextCodePath(null, pkgName, ".apk");
codeFileName = new File(installDir, apkName + ".apk").getPath();
@@ -5509,12 +5705,12 @@
String libraryPath;
SdInstallArgs(InstallParams params) {
- super(params.packageURI, params.observer,
- params.flags, params.installerPackageName);
+ super(params.packageURI, params.observer, params.flags, params.installerPackageName,
+ params.manifestDigest);
}
SdInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) {
- super(null, null, PackageManager.INSTALL_EXTERNAL, null);
+ super(null, null, PackageManager.INSTALL_EXTERNAL, null, null);
// Extract cid from fullCodePath
int eidx = fullCodePath.lastIndexOf("/");
String subStr1 = fullCodePath.substring(0, eidx);
@@ -5524,13 +5720,13 @@
}
SdInstallArgs(String cid) {
- super(null, null, PackageManager.INSTALL_EXTERNAL, null);
+ super(null, null, PackageManager.INSTALL_EXTERNAL, null, null);
this.cid = cid;
setCachePath(PackageHelper.getSdDir(cid));
}
SdInstallArgs(Uri packageURI, String cid) {
- super(packageURI, null, PackageManager.INSTALL_EXTERNAL, null);
+ super(packageURI, null, PackageManager.INSTALL_EXTERNAL, null, null);
this.cid = cid;
}
@@ -6152,6 +6348,26 @@
res.returnCode = pp.getParseError();
return;
}
+
+ /* If the installer passed in a manifest digest, compare it now. */
+ if (args.manifestDigest != null) {
+ if (DEBUG_INSTALL) {
+ final String parsedManifest = pkg.manifestDigest == null ? "null"
+ : pkg.manifestDigest.toString();
+ Slog.d(TAG, "Comparing manifests: " + args.manifestDigest.toString() + " vs. "
+ + parsedManifest);
+ }
+
+ if (!args.manifestDigest.equals(pkg.manifestDigest)) {
+ res.returnCode = PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
+ return;
+ }
+ } else if (DEBUG_INSTALL) {
+ final String parsedManifest = pkg.manifestDigest == null
+ ? "null" : pkg.manifestDigest.toString();
+ Slog.d(TAG, "manifestDigest was not present, but parser got: " + parsedManifest);
+ }
+
// Get rid of all references to package scan path via parser.
pp = null;
String oldCodePath = null;
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index d84f1e5..501c219 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -37,6 +37,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
+import android.content.pm.ManifestDigest;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
@@ -533,4 +534,22 @@
public void updateUserFlags(int id, int flags) {
throw new UnsupportedOperationException();
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
+ int flags, String installerPackageName, Uri verificationURI,
+ ManifestDigest manifestDigest) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void verifyPendingInstall(int id, boolean verified, String failureMessage) {
+ throw new UnsupportedOperationException();
+ }
}