summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java329
-rw-r--r--services/java/com/android/server/pm/PackageVerificationResponse.java28
-rw-r--r--services/java/com/android/server/pm/PackageVerificationState.java149
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java205
4 files changed, 650 insertions, 61 deletions
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 05f7cf0..eb135b7 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -72,6 +72,7 @@ import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.content.pm.ManifestDigest;
import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.VerifierInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -113,6 +114,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -158,6 +161,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private static final boolean DEBUG_INTENT_MATCHING = false;
private static final boolean DEBUG_PACKAGE_SCANNING = false;
private static final boolean DEBUG_APP_DIR_OBSERVER = false;
+ private static final boolean DEBUG_VERIFY = false;
static final boolean MULTIPLE_APPLICATION_UIDS = true;
private static final int RADIO_UID = Process.PHONE_UID;
@@ -208,6 +212,8 @@ public class PackageManagerService extends IPackageManager.Stub {
DEFAULT_CONTAINER_PACKAGE,
"com.android.defcontainer.DefaultContainerService");
+ private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
+
private static final String LIB_DIR_NAME = "lib";
static final String mTempContainerPrefix = "smdl2tmp";
@@ -349,7 +355,8 @@ public class PackageManagerService extends IPackageManager.Stub {
final HashSet<String> mProtectedBroadcasts = new HashSet<String>();
/** List of packages waiting for verification. */
- final SparseArray<InstallArgs> mPendingVerification = new SparseArray<InstallArgs>();
+ final SparseArray<PackageVerificationState> mPendingVerification
+ = new SparseArray<PackageVerificationState>();
final ArrayList<PackageParser.Package> mDeferredDexOpt =
new ArrayList<PackageParser.Package>();
@@ -427,6 +434,8 @@ public class PackageManagerService extends IPackageManager.Stub {
final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<PostInstallData>();
int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows
+ private final String mRequiredVerifierPackage;
+
class PackageHandler extends Handler {
private boolean mBound = false;
final ArrayList<HandlerParams> mPendingInstalls =
@@ -740,9 +749,10 @@ public class PackageManagerService extends IPackageManager.Stub {
} break;
case CHECK_PENDING_VERIFICATION: {
final int verificationId = msg.arg1;
- final InstallArgs args = mPendingVerification.get(verificationId);
+ final PackageVerificationState state = mPendingVerification.get(verificationId);
- if (args != null) {
+ if (state != null) {
+ final InstallArgs args = state.getInstallArgs();
Slog.i(TAG, "Validation timed out for " + args.packageURI.toString());
mPendingVerification.remove(verificationId);
@@ -756,31 +766,38 @@ public class PackageManagerService extends IPackageManager.Stub {
}
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) {
+ final PackageVerificationState state = mPendingVerification.get(verificationId);
+ if (state == null) {
Slog.w(TAG, "Invalid validation token " + verificationId + " received");
break;
}
- mPendingVerification.remove(verificationId);
+ final PackageVerificationResponse response = (PackageVerificationResponse) msg.obj;
- 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");
+ state.setVerifierResponse(response.callerUid, response.code);
+
+ if (state.isVerificationComplete()) {
+ mPendingVerification.remove(verificationId);
+
+ final InstallArgs args = state.getInstallArgs();
+
+ int ret;
+ if (state.isInstallAllowed()) {
+ 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;
}
- } else {
- ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
- }
- processPendingInstall(args, ret);
+ processPendingInstall(args, ret);
- mHandler.sendEmptyMessage(MCS_UNBIND);
+ mHandler.sendEmptyMessage(MCS_UNBIND);
+ }
break;
}
@@ -1134,10 +1151,49 @@ public class PackageManagerService extends IPackageManager.Stub {
// are all flushed. Not really needed, but keeps things nice and
// tidy.
Runtime.getRuntime().gc();
+
+ mRequiredVerifierPackage = getRequiredVerifierLPr();
} // synchronized (mPackages)
} // synchronized (mInstallLock)
}
+ private String getRequiredVerifierLPr() {
+ final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
+ final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
+ PackageManager.GET_DISABLED_COMPONENTS);
+
+ String requiredVerifier = null;
+
+ final int N = receivers.size();
+ for (int i = 0; i < N; i++) {
+ final ResolveInfo info = receivers.get(i);
+
+ if (info.activityInfo == null) {
+ continue;
+ }
+
+ final String packageName = info.activityInfo.packageName;
+
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps == null) {
+ continue;
+ }
+
+ if (!ps.grantedPermissions
+ .contains(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT)) {
+ continue;
+ }
+
+ if (requiredVerifier != null) {
+ throw new RuntimeException("There can be only one required verifier");
+ }
+
+ requiredVerifier = packageName;
+ }
+
+ return requiredVerifier;
+ }
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -4857,17 +4913,110 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
- public void verifyPendingInstall(int id, int verificationCode)
- throws RemoteException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, null);
-
+ public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
+ final PackageVerificationResponse response = new PackageVerificationResponse(
+ verificationCode, Binder.getCallingUid());
msg.arg1 = id;
- msg.arg2 = verificationCode;
+ msg.obj = response;
mHandler.sendMessage(msg);
}
+ private ComponentName matchComponentForVerifier(String packageName,
+ List<ResolveInfo> receivers) {
+ ActivityInfo targetReceiver = null;
+
+ final int NR = receivers.size();
+ for (int i = 0; i < NR; i++) {
+ final ResolveInfo info = receivers.get(i);
+ if (info.activityInfo == null) {
+ continue;
+ }
+
+ if (packageName.equals(info.activityInfo.packageName)) {
+ targetReceiver = info.activityInfo;
+ break;
+ }
+ }
+
+ if (targetReceiver == null) {
+ return null;
+ }
+
+ return new ComponentName(targetReceiver.packageName, targetReceiver.name);
+ }
+
+ private List<ComponentName> matchVerifiers(PackageInfoLite pkgInfo,
+ List<ResolveInfo> receivers, final PackageVerificationState verificationState) {
+ if (pkgInfo.verifiers.length == 0) {
+ return null;
+ }
+
+ final int N = pkgInfo.verifiers.length;
+ final List<ComponentName> sufficientVerifiers = new ArrayList<ComponentName>(N + 1);
+ for (int i = 0; i < N; i++) {
+ final VerifierInfo verifierInfo = pkgInfo.verifiers[i];
+
+ final ComponentName comp = matchComponentForVerifier(verifierInfo.packageName,
+ receivers);
+ if (comp == null) {
+ continue;
+ }
+
+ final int verifierUid = getUidForVerifier(verifierInfo);
+ if (verifierUid == -1) {
+ continue;
+ }
+
+ if (DEBUG_VERIFY) {
+ Slog.d(TAG, "Added sufficient verifier " + verifierInfo.packageName
+ + " with the correct signature");
+ }
+ sufficientVerifiers.add(comp);
+ verificationState.addSufficientVerifier(verifierUid);
+ }
+
+ return sufficientVerifiers;
+ }
+
+ private int getUidForVerifier(VerifierInfo verifierInfo) {
+ synchronized (mPackages) {
+ final PackageParser.Package pkg = mPackages.get(verifierInfo.packageName);
+ if (pkg == null) {
+ return -1;
+ } else if (pkg.mSignatures.length != 1) {
+ Slog.i(TAG, "Verifier package " + verifierInfo.packageName
+ + " has more than one signature; ignoring");
+ return -1;
+ }
+
+ /*
+ * If the public key of the package's signature does not match
+ * our expected public key, then this is a different package and
+ * we should skip.
+ */
+
+ final byte[] expectedPublicKey;
+ try {
+ final Signature verifierSig = pkg.mSignatures[0];
+ final PublicKey publicKey = verifierSig.getPublicKey();
+ expectedPublicKey = publicKey.getEncoded();
+ } catch (CertificateException e) {
+ return -1;
+ }
+
+ final byte[] actualPublicKey = verifierInfo.publicKey.getEncoded();
+
+ if (!Arrays.equals(actualPublicKey, expectedPublicKey)) {
+ Slog.i(TAG, "Verifier package " + verifierInfo.packageName
+ + " does not have the expected public key; ignoring");
+ return -1;
+ }
+
+ return pkg.applicationInfo.uid;
+ }
+ }
+
public void finishPackageInstall(int token) {
enforceSystemOrRoot("Only the system is allowed to finish installs");
@@ -5237,9 +5386,11 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
- boolean fwdLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
- boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0;
- boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0;
+ final boolean fwdLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
+ final boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0;
+ final boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0;
+ PackageInfoLite pkgLite = null;
+
if (onInt && onSd) {
// Check if both bits are set.
Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
@@ -5261,7 +5412,6 @@ public class PackageManagerService extends IPackageManager.Stub {
}
// Remote call to find out default install location
- final PackageInfoLite pkgLite;
try {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -5304,21 +5454,27 @@ public class PackageManagerService extends IPackageManager.Stub {
}
final InstallArgs args = createInstallArgs(this);
+ mArgs = args;
+
if (ret == PackageManager.INSTALL_SUCCEEDED) {
/*
* 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) {
+ final int requiredUid = mRequiredVerifierPackage == null ? -1
+ : getPackageUid(mRequiredVerifierPackage);
+ if (requiredUid != -1 && isVerificationEnabled()) {
+ 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 (DEBUG_VERIFY) {
Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
- + verification.toString());
+ + verification.toString() + " with " + pkgLite.verifiers.length
+ + " optional verifiers");
}
final int verificationId = mPendingVerificationToken++;
@@ -5335,35 +5491,70 @@ public class PackageManagerService extends IPackageManager.Stub {
verificationURI);
}
- mPendingVerification.append(verificationId, args);
+ final PackageVerificationState verificationState = new PackageVerificationState(
+ requiredUid, args);
+
+ mPendingVerification.append(verificationId, verificationState);
+
+ final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
+ receivers, verificationState);
/*
- * Send the intent to the registered verification agents,
- * but only start the verification timeout after the target
- * BroadcastReceivers have run.
+ * If any sufficient verifiers were listed in the package
+ * manifest, attempt to ask them.
*/
- 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);
+ if (sufficientVerifiers != null) {
+ final int N = sufficientVerifiers.size();
+ if (N == 0) {
+ Slog.i(TAG, "Additional verifiers required, but none installed.");
+ ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
+ } else {
+ for (int i = 0; i < N; i++) {
+ final ComponentName verifierComponent = sufficientVerifiers.get(i);
+
+ final Intent sufficientIntent = new Intent(verification);
+ sufficientIntent.setComponent(verifierComponent);
+
+ mContext.sendBroadcast(sufficientIntent);
+ }
+ }
+ }
+
+ final ComponentName requiredVerifierComponent = matchComponentForVerifier(
+ mRequiredVerifierPackage, receivers);
+ if (ret == PackageManager.INSTALL_SUCCEEDED
+ && mRequiredVerifierPackage != null) {
+ /*
+ * Send the intent to the required verification agent,
+ * but only start the verification timeout after the
+ * target BroadcastReceivers have run.
+ */
+ verification.setComponent(requiredVerifierComponent);
+ 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);
+
+ /*
+ * We don't want the copy to proceed until verification
+ * succeeds, so null out this field.
+ */
+ mArgs = null;
+ }
} else {
- // Create copy only if we are not in an erroneous state.
- // Remote call to initiate copy using temporary file
- mArgs = args;
+ /*
+ * No package verification is enabled, so immediately start
+ * the remote call to initiate copy using temporary file.
+ */
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;
@@ -7549,6 +7740,8 @@ public class PackageManagerService extends IPackageManager.Stub {
public static final int DUMP_PROVIDERS = 1 << 7;
+ public static final int DUMP_VERIFIERS = 1 << 8;
+
public static final int OPTION_SHOW_FILTERS = 1 << 0;
private int mTypes;
@@ -7641,6 +7834,7 @@ public class PackageManagerService extends IPackageManager.Stub {
pw.println(" p[ackages]: dump installed packages");
pw.println(" s[hared-users]: dump shared user IDs");
pw.println(" m[essages]: print collected runtime messages");
+ pw.println(" v[erifiers]: print package verifier info");
pw.println(" <package.name>: info about given package");
return;
} else if ("-f".equals(opt)) {
@@ -7673,11 +7867,24 @@ public class PackageManagerService extends IPackageManager.Stub {
dumpState.setDump(DumpState.DUMP_PROVIDERS);
} else if ("m".equals(cmd) || "messages".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_MESSAGES);
+ } else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_VERIFIERS);
}
}
// reader
synchronized (mPackages) {
+ if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
+ pw.println("Verifiers:");
+ pw.print(" Required: ");
+ pw.print(mRequiredVerifierPackage);
+ pw.print(" (uid=");
+ pw.print(getPackageUid(mRequiredVerifierPackage));
+ pw.println(")");
+ }
+
if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
if (dumpState.onTitlePrinted())
pw.println(" ");
diff --git a/services/java/com/android/server/pm/PackageVerificationResponse.java b/services/java/com/android/server/pm/PackageVerificationResponse.java
new file mode 100644
index 0000000..b2ae0dd
--- /dev/null
+++ b/services/java/com/android/server/pm/PackageVerificationResponse.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+public class PackageVerificationResponse {
+ public final int code;
+
+ public final int callerUid;
+
+ public PackageVerificationResponse(int code, int callerUid) {
+ this.code = code;
+ this.callerUid = callerUid;
+ }
+}
diff --git a/services/java/com/android/server/pm/PackageVerificationState.java b/services/java/com/android/server/pm/PackageVerificationState.java
new file mode 100644
index 0000000..e5b89c1
--- /dev/null
+++ b/services/java/com/android/server/pm/PackageVerificationState.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import com.android.server.pm.PackageManagerService.InstallArgs;
+
+import android.content.pm.PackageManager;
+import android.util.SparseBooleanArray;
+
+/**
+ * Tracks the package verification state for a particular package. Each package
+ * verification has a required verifier and zero or more sufficient verifiers.
+ * Only one of the sufficient verifier list must return affirmative to allow the
+ * package to be considered verified. If there are zero sufficient verifiers,
+ * then package verification is considered complete.
+ */
+class PackageVerificationState {
+ private final InstallArgs mArgs;
+
+ private final SparseBooleanArray mSufficientVerifierUids;
+
+ private final int mRequiredVerifierUid;
+
+ private boolean mSufficientVerificationComplete;
+
+ private boolean mSufficientVerificationPassed;
+
+ private boolean mRequiredVerificationComplete;
+
+ private boolean mRequiredVerificationPassed;
+
+ /**
+ * Create a new package verification state where {@code requiredVerifierUid}
+ * is the user ID for the package that must reply affirmative before things
+ * can continue.
+ *
+ * @param requiredVerifierUid user ID of required package verifier
+ * @param args
+ */
+ public PackageVerificationState(int requiredVerifierUid, InstallArgs args) {
+ mRequiredVerifierUid = requiredVerifierUid;
+ mArgs = args;
+ mSufficientVerifierUids = new SparseBooleanArray();
+ }
+
+ public InstallArgs getInstallArgs() {
+ return mArgs;
+ }
+
+ /**
+ * Add a verifier which is added to our sufficient list.
+ *
+ * @param uid user ID of sufficient verifier
+ */
+ public void addSufficientVerifier(int uid) {
+ mSufficientVerifierUids.put(uid, true);
+ }
+
+ /**
+ * Should be called when a verification is received from an agent so the
+ * state of the package verification can be tracked.
+ *
+ * @param uid user ID of the verifying agent
+ * @return {@code true} if the verifying agent actually exists in our list
+ */
+ public boolean setVerifierResponse(int uid, int code) {
+ if (uid == mRequiredVerifierUid) {
+ mRequiredVerificationComplete = true;
+ switch (code) {
+ case PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT:
+ mSufficientVerifierUids.clear();
+ // fall through
+ case PackageManager.VERIFICATION_ALLOW:
+ mRequiredVerificationPassed = true;
+ break;
+ default:
+ mRequiredVerificationPassed = false;
+ }
+ return true;
+ } else {
+ if (mSufficientVerifierUids.get(uid)) {
+ if (code == PackageManager.VERIFICATION_ALLOW) {
+ mSufficientVerificationComplete = true;
+ mSufficientVerificationPassed = true;
+ }
+
+ mSufficientVerifierUids.delete(uid);
+ if (mSufficientVerifierUids.size() == 0) {
+ mSufficientVerificationComplete = true;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether verification is considered complete. This means that the
+ * required verifier and at least one of the sufficient verifiers has
+ * returned a positive verification.
+ *
+ * @return {@code true} when verification is considered complete
+ */
+ public boolean isVerificationComplete() {
+ if (!mRequiredVerificationComplete) {
+ return false;
+ }
+
+ if (mSufficientVerifierUids.size() == 0) {
+ return true;
+ }
+
+ return mSufficientVerificationComplete;
+ }
+
+ /**
+ * Returns whether installation should be allowed. This should only be
+ * called after {@link #isVerificationComplete()} returns {@code true}.
+ *
+ * @return {@code true} if installation should be allowed
+ */
+ public boolean isInstallAllowed() {
+ if (!mRequiredVerificationPassed) {
+ return false;
+ }
+
+ if (mSufficientVerificationComplete) {
+ return mSufficientVerificationPassed;
+ }
+
+ return true;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
new file mode 100644
index 0000000..ebd3633
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.PackageManager;
+import com.android.server.pm.PackageVerificationState;
+
+import android.test.AndroidTestCase;
+
+public class PackageVerificationStateTest extends AndroidTestCase {
+ private static final int REQUIRED_UID = 1948;
+
+ private static final int SUFFICIENT_UID_1 = 1005;
+
+ private static final int SUFFICIENT_UID_2 = 8938;
+
+ public void testPackageVerificationState_OnlyRequiredVerifier_AllowedInstall() {
+ PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertTrue("Installation should be marked as allowed",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_OnlyRequiredVerifier_DeniedInstall() {
+ PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_REJECT);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertFalse("Installation should be marked as allowed",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_RequiredAndOneSufficient_RequiredDeniedInstall() {
+ PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.addSufficientVerifier(SUFFICIENT_UID_1);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(SUFFICIENT_UID_1, PackageManager.VERIFICATION_ALLOW);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_REJECT);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertFalse("Installation should be marked as allowed",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_RequiredAndOneSufficient_SufficientDeniedInstall() {
+ PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.addSufficientVerifier(SUFFICIENT_UID_1);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(SUFFICIENT_UID_1, PackageManager.VERIFICATION_REJECT);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertFalse("Installation should be marked as allowed",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_RequiredAndTwoSufficient_OneSufficientIsEnough() {
+ PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.addSufficientVerifier(SUFFICIENT_UID_1);
+ state.addSufficientVerifier(SUFFICIENT_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(SUFFICIENT_UID_1, PackageManager.VERIFICATION_ALLOW);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertTrue("Installation should be marked as allowed",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_RequiredAndTwoSufficient_SecondSufficientIsEnough() {
+ PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.addSufficientVerifier(SUFFICIENT_UID_1);
+ state.addSufficientVerifier(SUFFICIENT_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(SUFFICIENT_UID_1, PackageManager.VERIFICATION_REJECT);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(SUFFICIENT_UID_2, PackageManager.VERIFICATION_ALLOW);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertTrue("Installation should be marked as allowed",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_RequiredAndTwoSufficient_RequiredOverrides() {
+ PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.addSufficientVerifier(SUFFICIENT_UID_1);
+ state.addSufficientVerifier(SUFFICIENT_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID,
+ PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
+
+ assertTrue("Verification should be marked as complete immediately",
+ state.isVerificationComplete());
+
+ assertTrue("Installation should be marked as allowed",
+ state.isInstallAllowed());
+
+ state.setVerifierResponse(SUFFICIENT_UID_1, PackageManager.VERIFICATION_REJECT);
+
+ assertTrue("Verification should still be marked as completed",
+ state.isVerificationComplete());
+
+ assertTrue("Installation should be marked as allowed still",
+ state.isInstallAllowed());
+
+ state.setVerifierResponse(SUFFICIENT_UID_2, PackageManager.VERIFICATION_ALLOW);
+
+ assertTrue("Verification should still be complete",
+ state.isVerificationComplete());
+
+ assertTrue("Installation should be marked as allowed still",
+ state.isInstallAllowed());
+ }
+}