diff options
author | Kenny Root <kroot@google.com> | 2011-09-15 10:36:25 -0700 |
---|---|---|
committer | Kenny Root <kroot@google.com> | 2011-09-23 16:03:03 -0700 |
commit | 05ca4c90644921df9193d92b2abdc81ef77e4a62 (patch) | |
tree | 94c9e61b0c3d364f68a194c0a65199451410fc35 /services | |
parent | 15bbaeb0753f5336a5e8ee07e6f796657ecefb73 (diff) | |
download | frameworks_base-05ca4c90644921df9193d92b2abdc81ef77e4a62.zip frameworks_base-05ca4c90644921df9193d92b2abdc81ef77e4a62.tar.gz frameworks_base-05ca4c90644921df9193d92b2abdc81ef77e4a62.tar.bz2 |
Allow non-required package verifiers
* Verifiers can be specified in the AndroidManifest.xml
* Those verifiers can respond to the new Intent action
* PackageManager API for those verifiers: verifyPendingInstall
Change-Id: I4892bce2e6984871e6e93c60a1ca0dae145f5df5
Diffstat (limited to 'services')
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()); + } +} |