From 75147d5967fc1f78dc76292f96380e05c28e23a0 Mon Sep 17 00:00:00 2001 From: Song Pan Date: Tue, 19 Nov 2019 00:57:46 +0000 Subject: Add public API and stub implementations for app integrity manager (service). App integrity manager will accept rules from verifier and evaluate these rules during install (and accept/reject the install accordingly). For more information, see the linked bug (and the blocked bug). Design doc: go/integrity-component-android-infra Bug: 143689885 Change-Id: Ie965c6cc77987e06e9dea4be8fb49785a7bd08c4 Test: moving only --- api/system-current.txt | 146 +++++ core/java/android/content/Context.java | 8 + core/java/android/content/Intent.java | 16 + .../content/integrity/AppInstallMetadata.java | 182 ++++++ .../content/integrity/AppIntegrityManager.java | 103 ++++ .../android/content/integrity/AtomicFormula.java | 518 +++++++++++++++++ .../android/content/integrity/CompoundFormula.java | 223 ++++++++ core/java/android/content/integrity/Formula.java | 102 ++++ .../content/integrity/IAppIntegrityManager.aidl | 28 + core/java/android/content/integrity/Rule.aidl | 19 + core/java/android/content/integrity/Rule.java | 147 +++++ core/java/android/content/integrity/RuleSet.java | 92 ++++ core/res/AndroidManifest.xml | 1 + core/res/res/values/config.xml | 6 + core/res/res/values/symbols.xml | 1 + .../content/integrity/AtomicFormulaTest.java | 297 ++++++++++ .../content/integrity/CompoundFormulaTest.java | 247 +++++++++ .../src/android/content/integrity/RuleTest.java | 119 ++++ .../src/android/content/integrity/TEST_MAPPING | 12 + .../src/android/content/integrity/TestUtils.java | 51 ++ .../integrity/engine/RuleEvaluationEngine.java | 15 +- .../server/integrity/engine/RuleEvaluator.java | 26 +- .../server/integrity/engine/RuleLoader.java | 2 +- .../server/integrity/model/AppInstallMetadata.java | 182 ------ .../server/integrity/model/AtomicFormula.java | 518 ----------------- .../android/server/integrity/model/Formula.java | 102 ---- .../integrity/model/IntegrityCheckResult.java | 1 + .../server/integrity/model/OpenFormula.java | 224 -------- .../com/android/server/integrity/model/Rule.java | 147 ----- .../server/integrity/parser/RuleBinaryParser.java | 2 +- .../server/integrity/parser/RuleParser.java | 2 +- .../server/integrity/parser/RuleXmlParser.java | 41 +- .../integrity/serializer/RuleBinarySerializer.java | 2 +- .../integrity/serializer/RuleSerializer.java | 2 +- .../integrity/serializer/RuleXmlSerializer.java | 25 +- .../server/integrity/engine/RuleEvaluatorTest.java | 123 +++-- .../server/integrity/model/AtomicFormulaTest.java | 297 ---------- .../server/integrity/model/OpenFormulaTest.java | 234 -------- .../android/server/integrity/model/RuleTest.java | 119 ---- .../server/integrity/parser/RuleXmlParserTest.java | 612 ++++++++++++--------- .../serializer/RuleXmlSerializerTest.java | 28 +- 41 files changed, 2829 insertions(+), 2193 deletions(-) create mode 100644 core/java/android/content/integrity/AppInstallMetadata.java create mode 100644 core/java/android/content/integrity/AppIntegrityManager.java create mode 100644 core/java/android/content/integrity/AtomicFormula.java create mode 100644 core/java/android/content/integrity/CompoundFormula.java create mode 100644 core/java/android/content/integrity/Formula.java create mode 100644 core/java/android/content/integrity/IAppIntegrityManager.aidl create mode 100644 core/java/android/content/integrity/Rule.aidl create mode 100644 core/java/android/content/integrity/Rule.java create mode 100644 core/java/android/content/integrity/RuleSet.java create mode 100644 core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java create mode 100644 core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java create mode 100644 core/tests/coretests/src/android/content/integrity/RuleTest.java create mode 100644 core/tests/coretests/src/android/content/integrity/TEST_MAPPING create mode 100644 core/tests/coretests/src/android/content/integrity/TestUtils.java delete mode 100644 services/core/java/com/android/server/integrity/model/AppInstallMetadata.java delete mode 100644 services/core/java/com/android/server/integrity/model/AtomicFormula.java delete mode 100644 services/core/java/com/android/server/integrity/model/Formula.java delete mode 100644 services/core/java/com/android/server/integrity/model/OpenFormula.java delete mode 100644 services/core/java/com/android/server/integrity/model/Rule.java delete mode 100644 services/tests/servicestests/src/com/android/server/integrity/model/AtomicFormulaTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java diff --git a/api/system-current.txt b/api/system-current.txt index adfda2fe527a..83c03a363482 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1453,6 +1453,7 @@ package android.content { method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle); method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle); + field public static final String APP_INTEGRITY_SERVICE = "app_integrity"; field public static final String APP_PREDICTION_SERVICE = "app_prediction"; field public static final String BACKUP_SERVICE = "backup"; field public static final String BATTERY_STATS_SERVICE = "batterystats"; @@ -1505,6 +1506,7 @@ package android.content { field public static final String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS"; field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_SPECIAL_APP_ACCESSES = "android.intent.action.MANAGE_SPECIAL_APP_ACCESSES"; field public static final String ACTION_MASTER_CLEAR_NOTIFICATION = "android.intent.action.MASTER_CLEAR_NOTIFICATION"; + field public static final String ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION = "android.intent.action.PACKAGE_NEEDS_INTEGRITY_VERIFICATION"; field public static final String ACTION_PENDING_INCIDENT_REPORTS_CHANGED = "android.intent.action.PENDING_INCIDENT_REPORTS_CHANGED"; field public static final String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED"; field public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART"; @@ -1552,6 +1554,150 @@ package android.content { } +package android.content.integrity { + + public final class AppInstallMetadata { + method @NonNull public String getAppCertificate(); + method @Nullable public String getInstallerCertificate(); + method @Nullable public String getInstallerName(); + method @NonNull public String getPackageName(); + method public int getVersionCode(); + method public boolean isPreInstalled(); + } + + public static final class AppInstallMetadata.Builder { + ctor public AppInstallMetadata.Builder(); + method @NonNull public android.content.integrity.AppInstallMetadata build(); + method @NonNull public android.content.integrity.AppInstallMetadata.Builder setAppCertificate(@NonNull String); + method @NonNull public android.content.integrity.AppInstallMetadata.Builder setInstallerCertificate(@NonNull String); + method @NonNull public android.content.integrity.AppInstallMetadata.Builder setInstallerName(@NonNull String); + method @NonNull public android.content.integrity.AppInstallMetadata.Builder setIsPreInstalled(boolean); + method @NonNull public android.content.integrity.AppInstallMetadata.Builder setPackageName(@NonNull String); + method @NonNull public android.content.integrity.AppInstallMetadata.Builder setVersionCode(int); + } + + public class AppIntegrityManager { + method @NonNull public String getCurrentRuleSetProvider(); + method @NonNull public String getCurrentRuleSetVersion(); + method public void updateRuleSet(@NonNull android.content.integrity.RuleSet, @NonNull android.content.IntentSender); + field public static final String EXTRA_STATUS = "android.content.integrity.extra.STATUS"; + field public static final int STATUS_FAILURE = 1; // 0x1 + field public static final int STATUS_SUCCESS = 0; // 0x0 + } + + public abstract class AtomicFormula implements android.content.integrity.Formula { + ctor public AtomicFormula(@android.content.integrity.AtomicFormula.Key int); + method public int getKey(); + field public static final int APP_CERTIFICATE = 1; // 0x1 + field public static final int EQ = 0; // 0x0 + field public static final int GE = 4; // 0x4 + field public static final int GT = 3; // 0x3 + field public static final int INSTALLER_CERTIFICATE = 3; // 0x3 + field public static final int INSTALLER_NAME = 2; // 0x2 + field public static final int LE = 2; // 0x2 + field public static final int LT = 1; // 0x1 + field public static final int PACKAGE_NAME = 0; // 0x0 + field public static final int PRE_INSTALLED = 5; // 0x5 + field public static final int VERSION_CODE = 4; // 0x4 + } + + public static final class AtomicFormula.BooleanAtomicFormula extends android.content.integrity.AtomicFormula implements android.os.Parcelable { + ctor public AtomicFormula.BooleanAtomicFormula(@android.content.integrity.AtomicFormula.Key int, boolean); + method public int describeContents(); + method public int getTag(); + method public boolean getValue(); + method public boolean isSatisfied(@NonNull android.content.integrity.AppInstallMetadata); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class AtomicFormula.IntAtomicFormula extends android.content.integrity.AtomicFormula implements android.os.Parcelable { + ctor public AtomicFormula.IntAtomicFormula(@android.content.integrity.AtomicFormula.Key int, @android.content.integrity.AtomicFormula.Operator int, int); + method public int describeContents(); + method public int getOperator(); + method public int getTag(); + method public int getValue(); + method public boolean isSatisfied(@NonNull android.content.integrity.AppInstallMetadata); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + @IntDef({android.content.integrity.AtomicFormula.PACKAGE_NAME, android.content.integrity.AtomicFormula.APP_CERTIFICATE, android.content.integrity.AtomicFormula.INSTALLER_NAME, android.content.integrity.AtomicFormula.INSTALLER_CERTIFICATE, android.content.integrity.AtomicFormula.VERSION_CODE, android.content.integrity.AtomicFormula.PRE_INSTALLED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface AtomicFormula.Key { + } + + @IntDef({android.content.integrity.AtomicFormula.EQ, android.content.integrity.AtomicFormula.LT, android.content.integrity.AtomicFormula.LE, android.content.integrity.AtomicFormula.GT, android.content.integrity.AtomicFormula.GE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface AtomicFormula.Operator { + } + + public static final class AtomicFormula.StringAtomicFormula extends android.content.integrity.AtomicFormula implements android.os.Parcelable { + ctor public AtomicFormula.StringAtomicFormula(@android.content.integrity.AtomicFormula.Key int, @NonNull String, boolean); + method public int describeContents(); + method public boolean getIsHashedValue(); + method public int getTag(); + method @NonNull public String getValue(); + method public boolean isSatisfied(@NonNull android.content.integrity.AppInstallMetadata); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public final class CompoundFormula implements android.content.integrity.Formula android.os.Parcelable { + ctor public CompoundFormula(@android.content.integrity.CompoundFormula.Connector int, @NonNull java.util.List); + method public int describeContents(); + method @android.content.integrity.CompoundFormula.Connector public int getConnector(); + method @NonNull public java.util.List getFormulas(); + method public int getTag(); + method public boolean isSatisfied(@NonNull android.content.integrity.AppInstallMetadata); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int AND = 0; // 0x0 + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final int NOT = 2; // 0x2 + field public static final int OR = 1; // 0x1 + } + + @IntDef({android.content.integrity.CompoundFormula.AND, android.content.integrity.CompoundFormula.OR, android.content.integrity.CompoundFormula.NOT}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface CompoundFormula.Connector { + } + + public interface Formula { + method @android.content.integrity.Formula.Tag public int getTag(); + method public boolean isSatisfied(@NonNull android.content.integrity.AppInstallMetadata); + method @NonNull public static android.content.integrity.Formula readFromParcel(@NonNull android.os.Parcel); + method public static void writeToParcel(@NonNull android.content.integrity.Formula, @NonNull android.os.Parcel, int); + field public static final int BOOLEAN_ATOMIC_FORMULA_TAG = 3; // 0x3 + field public static final int COMPOUND_FORMULA_TAG = 0; // 0x0 + field public static final int INT_ATOMIC_FORMULA_TAG = 2; // 0x2 + field public static final int STRING_ATOMIC_FORMULA_TAG = 1; // 0x1 + } + + @IntDef({android.content.integrity.Formula.COMPOUND_FORMULA_TAG, android.content.integrity.Formula.STRING_ATOMIC_FORMULA_TAG, android.content.integrity.Formula.INT_ATOMIC_FORMULA_TAG, android.content.integrity.Formula.BOOLEAN_ATOMIC_FORMULA_TAG}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface Formula.Tag { + } + + public final class Rule implements android.os.Parcelable { + ctor public Rule(@NonNull android.content.integrity.Formula, @android.content.integrity.Rule.Effect int); + method public int describeContents(); + method @android.content.integrity.Rule.Effect public int getEffect(); + method @NonNull public android.content.integrity.Formula getFormula(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final int DENY = 0; // 0x0 + field public static final int FORCE_ALLOW = 1; // 0x1 + } + + @IntDef({android.content.integrity.Rule.DENY, android.content.integrity.Rule.FORCE_ALLOW}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface Rule.Effect { + } + + public class RuleSet { + method @NonNull public java.util.List getRules(); + method @NonNull public String getVersion(); + } + + public static class RuleSet.Builder { + ctor public RuleSet.Builder(); + method @NonNull public android.content.integrity.RuleSet.Builder addRules(@NonNull java.util.List); + method @NonNull public android.content.integrity.RuleSet build(); + method @NonNull public android.content.integrity.RuleSet.Builder setVersion(@NonNull String); + } + +} + package android.content.om { public final class OverlayInfo implements android.os.Parcelable { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 0fdb51359cf9..0c6fdc079321 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4916,6 +4916,14 @@ public abstract class Context { */ public static final String APP_SEARCH_SERVICE = "app_search"; + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.content.integrity.AppIntegrityManager}. + * @hide + */ + @SystemApi + public static final String APP_INTEGRITY_SERVICE = "app_integrity"; + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index ca374f938f1e..40aca0ef2033 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4417,6 +4417,22 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_VIEW_LOCUS = "android.intent.action.VIEW_LOCUS"; + /** + * Broadcast Action: Sent to the integrity component when a package + * needs to be verified. The data contains the package URI along with other relevant + * information. + * + *

+ * This is a protected intent that can only be sent by the system. + *

+ * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION = + "android.intent.action.PACKAGE_NEEDS_INTEGRITY_VERIFICATION"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent categories (see addCategory()). diff --git a/core/java/android/content/integrity/AppInstallMetadata.java b/core/java/android/content/integrity/AppInstallMetadata.java new file mode 100644 index 000000000000..c9634758f63f --- /dev/null +++ b/core/java/android/content/integrity/AppInstallMetadata.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2019 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 android.content.integrity; + +import static com.android.internal.util.Preconditions.checkArgument; +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * The app install metadata. + * + *

The integrity component retrieves metadata for app installs from package manager, passing it + * to the rule evaluation engine to evaluate the metadata against the rules. + * + *

Instances of this class are immutable. + * + * @hide + */ +@SystemApi +@VisibleForTesting +public final class AppInstallMetadata { + private final String mPackageName; + // Raw string encoding for the SHA-256 hash of the certificate of the app. + private final String mAppCertificate; + private final String mInstallerName; + // Raw string encoding for the SHA-256 hash of the certificate of the installer. + private final String mInstallerCertificate; + private final int mVersionCode; + private final boolean mIsPreInstalled; + + private AppInstallMetadata(Builder builder) { + this.mPackageName = builder.mPackageName; + this.mAppCertificate = builder.mAppCertificate; + this.mInstallerName = builder.mInstallerName; + this.mInstallerCertificate = builder.mInstallerCertificate; + this.mVersionCode = builder.mVersionCode; + this.mIsPreInstalled = builder.mIsPreInstalled; + } + + @NonNull + public String getPackageName() { + return mPackageName; + } + + @NonNull + public String getAppCertificate() { + return mAppCertificate; + } + + @Nullable + public String getInstallerName() { + return mInstallerName; + } + + @Nullable + public String getInstallerCertificate() { + return mInstallerCertificate; + } + + /** @see AppInstallMetadata.Builder#setVersionCode(int) */ + public int getVersionCode() { + return mVersionCode; + } + + /** @see AppInstallMetadata.Builder#setIsPreInstalled(boolean) */ + public boolean isPreInstalled() { + return mIsPreInstalled; + } + + /** Builder class for constructing {@link AppInstallMetadata} objects. */ + public static final class Builder { + private String mPackageName; + private String mAppCertificate; + private String mInstallerName; + private String mInstallerCertificate; + private int mVersionCode; + private boolean mIsPreInstalled; + + /** + * Set package name of the app to be installed. + * + * @see AppInstallMetadata#getPackageName() + */ + @NonNull + public Builder setPackageName(@NonNull String packageName) { + this.mPackageName = checkNotNull(packageName); + return this; + } + + /** + * Set certificate of the app to be installed. + * + *

It is represented as the raw string encoding for the SHA-256 hash of the certificate + * of the app. + * + * @see AppInstallMetadata#getAppCertificate() + */ + @NonNull + public Builder setAppCertificate(@NonNull String appCertificate) { + this.mAppCertificate = checkNotNull(appCertificate); + return this; + } + + /** + * Set name of the installer installing the app. + * + * @see AppInstallMetadata#getInstallerName() + */ + @NonNull + public Builder setInstallerName(@NonNull String installerName) { + this.mInstallerName = checkNotNull(installerName); + return this; + } + + /** + * Set certificate of the installer installing the app. + * + *

It is represented as the raw string encoding for the SHA-256 hash of the certificate + * of the installer. + * + * @see AppInstallMetadata#getInstallerCertificate() + */ + @NonNull + public Builder setInstallerCertificate(@NonNull String installerCertificate) { + this.mInstallerCertificate = checkNotNull(installerCertificate); + return this; + } + + /** + * Set version code of the app to be installed. + * + * @see AppInstallMetadata#getVersionCode() + */ + @NonNull + public Builder setVersionCode(int versionCode) { + this.mVersionCode = versionCode; + return this; + } + + /** + * Set whether the app is pre-installed on the device or not. + * + * @see AppInstallMetadata#isPreInstalled() + */ + @NonNull + public Builder setIsPreInstalled(boolean isPreInstalled) { + this.mIsPreInstalled = isPreInstalled; + return this; + } + + /** + * Build {@link AppInstallMetadata}. + * + * @throws IllegalArgumentException if package name or app certificate is null + */ + @NonNull + public AppInstallMetadata build() { + checkArgument(mPackageName != null); + checkArgument(mAppCertificate != null); + return new AppInstallMetadata(this); + } + } +} diff --git a/core/java/android/content/integrity/AppIntegrityManager.java b/core/java/android/content/integrity/AppIntegrityManager.java new file mode 100644 index 000000000000..e53ef66020f1 --- /dev/null +++ b/core/java/android/content/integrity/AppIntegrityManager.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 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 android.content.integrity; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; +import android.content.IntentSender; +import android.content.pm.ParceledListSlice; +import android.os.RemoteException; + +/** + * Class for pushing rules used to check the integrity of app installs. + * + *

Note: applications using methods of this class must be a system app and have their package + * name whitelisted as an integrity rule provider. Otherwise a {@link SecurityException} will be + * thrown. + * + * @hide + */ +@SystemApi +@SystemService(Context.APP_INTEGRITY_SERVICE) +public class AppIntegrityManager { + + /** The operation succeeded. */ + public static final int STATUS_SUCCESS = 0; + + /** The operation failed. */ + public static final int STATUS_FAILURE = 1; + + /** + * Current status of an operation. Will be one of {@link #STATUS_SUCCESS}, {@link + * #STATUS_FAILURE}. + * + *

More information about a status may be available through additional extras; see the + * individual status documentation for details. + * + * @see android.content.Intent#getIntExtra(String, int) + */ + public static final String EXTRA_STATUS = "android.content.integrity.extra.STATUS"; + + IAppIntegrityManager mManager; + + /** @hide */ + public AppIntegrityManager(IAppIntegrityManager manager) { + mManager = manager; + } + + /** + * Update the rules to evaluate during install time. + * + * @param updateRequest request containing the data of the rule set update + * @param statusReceiver Called when the state of the session changes. Intents sent to this + * receiver contain {@link #EXTRA_STATUS}. Refer to the individual status codes on how to + * handle them. + */ + public void updateRuleSet( + @NonNull RuleSet updateRequest, @NonNull IntentSender statusReceiver) { + try { + mManager.updateRuleSet( + updateRequest.getVersion(), + new ParceledListSlice<>(updateRequest.getRules()), + statusReceiver); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** Get the current version of the rule set. */ + @NonNull + public String getCurrentRuleSetVersion() { + try { + return mManager.getCurrentRuleSetVersion(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** Get the name of the package that provided the current rule set. */ + @NonNull + public String getCurrentRuleSetProvider() { + try { + return mManager.getCurrentRuleSetProvider(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } +} diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java new file mode 100644 index 000000000000..c8e164f1f232 --- /dev/null +++ b/core/java/android/content/integrity/AtomicFormula.java @@ -0,0 +1,518 @@ +/* + * Copyright (C) 2019 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 android.content.integrity; + +import static com.android.internal.util.Preconditions.checkArgument; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Represents a simple formula consisting of an app install metadata field and a value. + * + *

Instances of this class are immutable. + * + * @hide + */ +@SystemApi +@VisibleForTesting +public abstract class AtomicFormula implements Formula { + + private static final String TAG = "AtomicFormula"; + + @IntDef( + value = { + PACKAGE_NAME, + APP_CERTIFICATE, + INSTALLER_NAME, + INSTALLER_CERTIFICATE, + VERSION_CODE, + PRE_INSTALLED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Key {} + + @IntDef(value = {EQ, LT, LE, GT, GE}) + @Retention(RetentionPolicy.SOURCE) + public @interface Operator {} + + /** + * Package name of the app. + * + *

Can only be used in {@link StringAtomicFormula}. + */ + public static final int PACKAGE_NAME = 0; + + /** + * SHA-256 of the app certificate of the app. + * + *

Can only be used in {@link StringAtomicFormula}. + */ + public static final int APP_CERTIFICATE = 1; + + /** + * Package name of the installer. Will be empty string if installed by the system (e.g., adb). + * + *

Can only be used in {@link StringAtomicFormula}. + */ + public static final int INSTALLER_NAME = 2; + + /** + * SHA-256 of the cert of the installer. Will be empty string if installed by the system (e.g., + * adb). + * + *

Can only be used in {@link StringAtomicFormula}. + */ + public static final int INSTALLER_CERTIFICATE = 3; + + /** + * Version code of the app. + * + *

Can only be used in {@link IntAtomicFormula}. + */ + public static final int VERSION_CODE = 4; + + /** + * If the app is pre-installed on the device. + * + *

Can only be used in {@link BooleanAtomicFormula}. + */ + public static final int PRE_INSTALLED = 5; + + public static final int EQ = 0; + public static final int LT = 1; + public static final int LE = 2; + public static final int GT = 3; + public static final int GE = 4; + + private final @Key int mKey; + + public AtomicFormula(@Key int key) { + checkArgument(isValidKey(key), String.format("Unknown key: %d", key)); + mKey = key; + } + + /** An {@link AtomicFormula} with an key and int value. */ + public static final class IntAtomicFormula extends AtomicFormula implements Parcelable { + private final int mValue; + private final @Operator int mOperator; + + /** + * Constructs a new {@link IntAtomicFormula}. + * + *

This formula will hold if and only if the corresponding information of an install + * specified by {@code key} is of the correct relationship to {@code value} as specified by + * {@code operator}. + * + * @throws IllegalArgumentException if {@code key} cannot be used with integer value + */ + public IntAtomicFormula(@Key int key, @Operator int operator, int value) { + super(key); + checkArgument( + key == VERSION_CODE, + String.format("Key %s cannot be used with IntAtomicFormula", keyToString(key))); + checkArgument(isValidOperator(operator), + String.format("Unknown operator: %d", operator)); + mOperator = operator; + mValue = value; + } + + IntAtomicFormula(Parcel in) { + super(in.readInt()); + mValue = in.readInt(); + mOperator = in.readInt(); + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public IntAtomicFormula createFromParcel(Parcel in) { + return new IntAtomicFormula(in); + } + + @Override + public IntAtomicFormula[] newArray(int size) { + return new IntAtomicFormula[size]; + } + }; + + @Override + public boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) { + int metadataValue = getMetadataValueByKey(appInstallMetadata); + switch (mOperator) { + case EQ: + return metadataValue == mValue; + case LE: + return metadataValue <= mValue; + case LT: + return metadataValue < mValue; + case GE: + return metadataValue >= mValue; + case GT: + return metadataValue > mValue; + default: + Slog.i(TAG, String.format("Unexpected operator %d", mOperator)); + return false; + } + } + + @Override + public int getTag() { + return Formula.INT_ATOMIC_FORMULA_TAG; + } + + @Override + public String toString() { + return String.format( + "(%s %s %s)", keyToString(getKey()), operatorToString(mOperator), mValue); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + IntAtomicFormula that = (IntAtomicFormula) o; + return getKey() == that.getKey() + && mValue == that.mValue + && mOperator == that.mOperator; + } + + @Override + public int hashCode() { + return Objects.hash(getKey(), mOperator, mValue); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(getKey()); + dest.writeInt(mValue); + dest.writeInt(mOperator); + } + + public int getValue() { + return mValue; + } + + public int getOperator() { + return mOperator; + } + + private int getMetadataValueByKey(AppInstallMetadata appInstallMetadata) { + switch (getKey()) { + case VERSION_CODE: + return appInstallMetadata.getVersionCode(); + default: + throw new IllegalStateException( + "Unexpected key in IntAtomicFormula" + getKey()); + } + } + + private static boolean isValidOperator(int operator) { + return operator == EQ + || operator == LT + || operator == LE + || operator == GT + || operator == GE; + } + } + + /** An {@link AtomicFormula} with a key and string value. */ + public static final class StringAtomicFormula extends AtomicFormula implements Parcelable { + private final String mValue; + // Indicates whether the value is the actual value or the hashed value. + private final boolean mIsHashedValue; + + /** + * Constructs a new {@link StringAtomicFormula}. + * + *

This formula will hold if and only if the corresponding information of an install + * specified by {@code key} equals {@code value}. + * + * @throws IllegalArgumentException if {@code key} cannot be used with string value + */ + public StringAtomicFormula(@Key int key, @NonNull String value, boolean isHashedValue) { + super(key); + mIsHashedValue = isHashedValue; + checkArgument( + key == PACKAGE_NAME + || key == APP_CERTIFICATE + || key == INSTALLER_CERTIFICATE + || key == INSTALLER_NAME, + String.format( + "Key %s cannot be used with StringAtomicFormula", keyToString(key))); + mValue = value; + } + + StringAtomicFormula(Parcel in) { + super(in.readInt()); + mValue = in.readStringNoHelper(); + mIsHashedValue = in.readByte() != 0; + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public StringAtomicFormula createFromParcel(Parcel in) { + return new StringAtomicFormula(in); + } + + @Override + public StringAtomicFormula[] newArray(int size) { + return new StringAtomicFormula[size]; + } + }; + + @Override + public boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) { + String metadataValue = getMetadataValueByKey(appInstallMetadata); + return metadataValue.equals(mValue); + } + + @Override + public int getTag() { + return Formula.STRING_ATOMIC_FORMULA_TAG; + } + + @Override + public String toString() { + return String.format("(%s %s %s)", keyToString(getKey()), operatorToString(EQ), mValue); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StringAtomicFormula that = (StringAtomicFormula) o; + return getKey() == that.getKey() && Objects.equals(mValue, that.mValue); + } + + @Override + public int hashCode() { + return Objects.hash(getKey(), mValue); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(getKey()); + dest.writeStringNoHelper(mValue); + dest.writeByte((byte) (mIsHashedValue ? 1 : 0)); + } + + @NonNull + public String getValue() { + return mValue; + } + + public boolean getIsHashedValue() { + return mIsHashedValue; + } + + private String getMetadataValueByKey(AppInstallMetadata appInstallMetadata) { + switch (getKey()) { + case PACKAGE_NAME: + return appInstallMetadata.getPackageName(); + case APP_CERTIFICATE: + return appInstallMetadata.getAppCertificate(); + case INSTALLER_CERTIFICATE: + return appInstallMetadata.getInstallerCertificate(); + case INSTALLER_NAME: + return appInstallMetadata.getInstallerName(); + default: + throw new IllegalStateException( + "Unexpected key in StringAtomicFormula: " + getKey()); + } + } + } + + /** An {@link AtomicFormula} with a key and boolean value. */ + public static final class BooleanAtomicFormula extends AtomicFormula implements Parcelable { + private final boolean mValue; + + /** + * Constructs a new {@link BooleanAtomicFormula}. + * + *

This formula will hold if and only if the corresponding information of an install + * specified by {@code key} equals {@code value}. + * + * @throws IllegalArgumentException if {@code key} cannot be used with boolean value + */ + public BooleanAtomicFormula(@Key int key, boolean value) { + super(key); + checkArgument( + key == PRE_INSTALLED, + String.format( + "Key %s cannot be used with BooleanAtomicFormula", keyToString(key))); + mValue = value; + } + + BooleanAtomicFormula(Parcel in) { + super(in.readInt()); + mValue = in.readByte() != 0; + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public BooleanAtomicFormula createFromParcel(Parcel in) { + return new BooleanAtomicFormula(in); + } + + @Override + public BooleanAtomicFormula[] newArray(int size) { + return new BooleanAtomicFormula[size]; + } + }; + + @Override + public boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) { + boolean metadataValue = getMetadataValueByKey(appInstallMetadata); + return metadataValue == mValue; + } + + @Override + public int getTag() { + return Formula.BOOLEAN_ATOMIC_FORMULA_TAG; + } + + @Override + public String toString() { + return String.format("(%s %s %s)", keyToString(getKey()), operatorToString(EQ), mValue); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BooleanAtomicFormula that = (BooleanAtomicFormula) o; + return getKey() == that.getKey() && mValue == that.mValue; + } + + @Override + public int hashCode() { + return Objects.hash(getKey(), mValue); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(getKey()); + dest.writeByte((byte) (mValue ? 1 : 0)); + } + + public boolean getValue() { + return mValue; + } + + private boolean getMetadataValueByKey(AppInstallMetadata appInstallMetadata) { + switch (getKey()) { + case PRE_INSTALLED: + return appInstallMetadata.isPreInstalled(); + default: + throw new IllegalStateException( + "Unexpected key in BooleanAtomicFormula: " + getKey()); + } + } + } + + public int getKey() { + return mKey; + } + + static String keyToString(int key) { + switch (key) { + case PACKAGE_NAME: + return "PACKAGE_NAME"; + case APP_CERTIFICATE: + return "APP_CERTIFICATE"; + case VERSION_CODE: + return "VERSION_CODE"; + case INSTALLER_NAME: + return "INSTALLER_NAME"; + case INSTALLER_CERTIFICATE: + return "INSTALLER_CERTIFICATE"; + case PRE_INSTALLED: + return "PRE_INSTALLED"; + default: + throw new IllegalArgumentException("Unknown key " + key); + } + } + + static String operatorToString(int op) { + switch (op) { + case EQ: + return "EQ"; + case LT: + return "LT"; + case LE: + return "LE"; + case GT: + return "GT"; + case GE: + return "GE"; + default: + throw new IllegalArgumentException("Unknown operator " + op); + } + } + + private static boolean isValidKey(int key) { + return key == PACKAGE_NAME + || key == APP_CERTIFICATE + || key == VERSION_CODE + || key == INSTALLER_NAME + || key == INSTALLER_CERTIFICATE + || key == PRE_INSTALLED; + } +} diff --git a/core/java/android/content/integrity/CompoundFormula.java b/core/java/android/content/integrity/CompoundFormula.java new file mode 100644 index 000000000000..53a99534906c --- /dev/null +++ b/core/java/android/content/integrity/CompoundFormula.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2019 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 android.content.integrity; + +import static com.android.internal.util.Preconditions.checkArgument; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Represents a compound formula formed by joining other simple and complex formulas with boolean + * connectors. + * + *

Instances of this class are immutable. + * + * @hide + */ +@SystemApi +@VisibleForTesting +public final class CompoundFormula implements Formula, Parcelable { + private static final String TAG = "OpenFormula"; + + @IntDef( + value = { + AND, OR, NOT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Connector {} + + /** Boolean AND operator. */ + public static final int AND = 0; + + /** Boolean OR operator. */ + public static final int OR = 1; + + /** Boolean NOT operator. */ + public static final int NOT = 2; + + private final @Connector int mConnector; + private final @NonNull List mFormulas; + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public CompoundFormula createFromParcel(Parcel in) { + return new CompoundFormula(in); + } + + @Override + public CompoundFormula[] newArray(int size) { + return new CompoundFormula[size]; + } + }; + + /** + * Create a new formula from operator and operands. + * + * @throws IllegalArgumentException if the number of operands is not matching the requirements + * for that operator (at least 2 for {@link #AND} and {@link #OR}, 1 for {@link #NOT}). + */ + public CompoundFormula(@Connector int connector, @NonNull List formulas) { + checkArgument( + isValidConnector(connector), String.format("Unknown connector: %d", connector)); + validateFormulas(connector, formulas); + this.mConnector = connector; + this.mFormulas = Collections.unmodifiableList(formulas); + } + + CompoundFormula(Parcel in) { + mConnector = in.readInt(); + int length = in.readInt(); + checkArgument(length >= 0, "Must have non-negative length. Got " + length); + mFormulas = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + mFormulas.add(Formula.readFromParcel(in)); + } + validateFormulas(mConnector, mFormulas); + } + + public @Connector int getConnector() { + return mConnector; + } + + @NonNull + public List getFormulas() { + return mFormulas; + } + + @Override + public boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) { + switch (mConnector) { + case NOT: + return !mFormulas.get(0).isSatisfied(appInstallMetadata); + case AND: + return mFormulas.stream() + .allMatch(formula -> formula.isSatisfied(appInstallMetadata)); + case OR: + return mFormulas.stream() + .anyMatch(formula -> formula.isSatisfied(appInstallMetadata)); + default: + Slog.i(TAG, "Unknown connector " + mConnector); + return false; + } + } + + @Override + public int getTag() { + return Formula.COMPOUND_FORMULA_TAG; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (mFormulas.size() == 1) { + sb.append(String.format("%s ", connectorToString(mConnector))); + sb.append(mFormulas.get(0).toString()); + } else { + for (int i = 0; i < mFormulas.size(); i++) { + if (i > 0) { + sb.append(String.format(" %s ", connectorToString(mConnector))); + } + sb.append(mFormulas.get(i).toString()); + } + } + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CompoundFormula that = (CompoundFormula) o; + return mConnector == that.mConnector && mFormulas.equals(that.mFormulas); + } + + @Override + public int hashCode() { + return Objects.hash(mConnector, mFormulas); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mConnector); + dest.writeInt(mFormulas.size()); + for (Formula formula : mFormulas) { + Formula.writeToParcel(formula, dest, flags); + } + } + + private static void validateFormulas(@Connector int connector, List formulas) { + switch (connector) { + case AND: + case OR: + checkArgument( + formulas.size() >= 2, + String.format( + "Connector %s must have at least 2 formulas", + connectorToString(connector))); + break; + case NOT: + checkArgument( + formulas.size() == 1, + String.format( + "Connector %s must have 1 formula only", + connectorToString(connector))); + break; + } + } + + private static String connectorToString(int connector) { + switch (connector) { + case AND: + return "AND"; + case OR: + return "OR"; + case NOT: + return "NOT"; + default: + throw new IllegalArgumentException("Unknown connector " + connector); + } + } + + private static boolean isValidConnector(int connector) { + return connector == AND || connector == OR || connector == NOT; + } +} diff --git a/core/java/android/content/integrity/Formula.java b/core/java/android/content/integrity/Formula.java new file mode 100644 index 000000000000..030ff6b66e1f --- /dev/null +++ b/core/java/android/content/integrity/Formula.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019 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 android.content.integrity; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.content.integrity.AtomicFormula.BooleanAtomicFormula; +import android.content.integrity.AtomicFormula.IntAtomicFormula; +import android.content.integrity.AtomicFormula.StringAtomicFormula; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Represents a rule logic/content. + * + * @hide + */ +@SystemApi +@VisibleForTesting +public interface Formula { + @IntDef( + value = { + COMPOUND_FORMULA_TAG, + STRING_ATOMIC_FORMULA_TAG, + INT_ATOMIC_FORMULA_TAG, + BOOLEAN_ATOMIC_FORMULA_TAG + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Tag {} + + int COMPOUND_FORMULA_TAG = 0; + int STRING_ATOMIC_FORMULA_TAG = 1; + int INT_ATOMIC_FORMULA_TAG = 2; + int BOOLEAN_ATOMIC_FORMULA_TAG = 3; + + /** + * Returns if this formula can be satisfied by substituting the corresponding information of + * {@code appInstallMetadata} into the formula. + */ + boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata); + + /** Returns the tag that identifies the current class. */ + @Tag int getTag(); + + /** + * Write a {@link Formula} to {@link android.os.Parcel}. + * + *

This helper method is needed because non-final class/interface are not allowed to be + * {@link Parcelable}. + * + * @throws IllegalArgumentException if {@link Formula} is not a recognized subclass + */ + static void writeToParcel(@NonNull Formula formula, @NonNull Parcel dest, int flags) { + dest.writeInt(formula.getTag()); + ((Parcelable) formula).writeToParcel(dest, flags); + } + + /** + * Read a {@link Formula} from a {@link android.os.Parcel}. + * + *

We need this (hacky) helper method because non-final class/interface cannot be {@link + * Parcelable} (api lint error). + * + * @throws IllegalArgumentException if the parcel cannot be parsed + */ + @NonNull + static Formula readFromParcel(@NonNull Parcel in) { + int tag = in.readInt(); + switch (tag) { + case COMPOUND_FORMULA_TAG: + return CompoundFormula.CREATOR.createFromParcel(in); + case STRING_ATOMIC_FORMULA_TAG: + return StringAtomicFormula.CREATOR.createFromParcel(in); + case INT_ATOMIC_FORMULA_TAG: + return IntAtomicFormula.CREATOR.createFromParcel(in); + case BOOLEAN_ATOMIC_FORMULA_TAG: + return BooleanAtomicFormula.CREATOR.createFromParcel(in); + default: + throw new IllegalArgumentException("Unknown formula tag " + tag); + } + } +} diff --git a/core/java/android/content/integrity/IAppIntegrityManager.aidl b/core/java/android/content/integrity/IAppIntegrityManager.aidl new file mode 100644 index 000000000000..6b73fd70bb99 --- /dev/null +++ b/core/java/android/content/integrity/IAppIntegrityManager.aidl @@ -0,0 +1,28 @@ +/* +** +** Copyright 2019, 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 android.content.integrity; + +import android.content.integrity.Rule; +import android.content.IntentSender; +import android.content.pm.ParceledListSlice; + +/** @hide */ +interface IAppIntegrityManager { + void updateRuleSet(String version, in ParceledListSlice rules, in IntentSender statusReceiver); + String getCurrentRuleSetVersion(); + String getCurrentRuleSetProvider(); +} diff --git a/core/java/android/content/integrity/Rule.aidl b/core/java/android/content/integrity/Rule.aidl new file mode 100644 index 000000000000..a6634ee67905 --- /dev/null +++ b/core/java/android/content/integrity/Rule.aidl @@ -0,0 +1,19 @@ +/* +** +** Copyright 2007, 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 android.content.integrity; + +parcelable Rule; diff --git a/core/java/android/content/integrity/Rule.java b/core/java/android/content/integrity/Rule.java new file mode 100644 index 000000000000..914f1479edf3 --- /dev/null +++ b/core/java/android/content/integrity/Rule.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2019 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 android.content.integrity; + +import static com.android.internal.util.Preconditions.checkArgument; +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Represent rules to be used in the rule evaluation engine to match against app installs. + * + *

Instances of this class are immutable. + * + * @hide + */ +@SystemApi +@VisibleForTesting +public final class Rule implements Parcelable { + + @IntDef( + value = { + DENY, + FORCE_ALLOW, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Effect {} + + /** If this rule matches the install, the install should be denied. */ + public static final int DENY = 0; + + /** + * If this rule matches the install, the install will be allowed regardless of other matched + * rules. + */ + public static final int FORCE_ALLOW = 1; + + private final @NonNull Formula mFormula; + private final @Effect int mEffect; + + public Rule(@NonNull Formula formula, @Effect int effect) { + checkArgument(isValidEffect(effect), String.format("Unknown effect: %d", effect)); + this.mFormula = checkNotNull(formula); + this.mEffect = effect; + } + + Rule(Parcel in) { + mFormula = Formula.readFromParcel(in); + mEffect = in.readInt(); + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public Rule createFromParcel(Parcel in) { + return new Rule(in); + } + + @Override + public Rule[] newArray(int size) { + return new Rule[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + Formula.writeToParcel(mFormula, dest, flags); + dest.writeInt(mEffect); + } + + @NonNull + public Formula getFormula() { + return mFormula; + } + + public @Effect int getEffect() { + return mEffect; + } + + @Override + public String toString() { + return String.format("Rule: %s, %s", mFormula, effectToString(mEffect)); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Rule that = (Rule) o; + return mEffect == that.mEffect && Objects.equals(mFormula, that.mFormula); + } + + @Override + public int hashCode() { + return Objects.hash(mFormula, mEffect); + } + + private static String effectToString(int effect) { + switch (effect) { + case DENY: + return "DENY"; + case FORCE_ALLOW: + return "FORCE_ALLOW"; + default: + throw new IllegalArgumentException("Unknown effect " + effect); + } + } + + private static boolean isValidEffect(int effect) { + return effect == DENY + || effect == FORCE_ALLOW; + } +} diff --git a/core/java/android/content/integrity/RuleSet.java b/core/java/android/content/integrity/RuleSet.java new file mode 100644 index 000000000000..a78f8c97d553 --- /dev/null +++ b/core/java/android/content/integrity/RuleSet.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 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 android.content.integrity; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.annotation.NonNull; +import android.annotation.SystemApi; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Immutable data class encapsulating all parameters of a rule set. + * + * @hide + */ +@SystemApi +public class RuleSet { + private final String mVersion; + private final List mRules; + + private RuleSet(String version, List rules) { + mVersion = version; + mRules = Collections.unmodifiableList(rules); + } + + /** @see Builder#setVersion(String). */ + @NonNull + public String getVersion() { + return mVersion; + } + + /** @see Builder#addRules(List). */ + @NonNull + public List getRules() { + return mRules; + } + + /** Builder class for RuleSetUpdateRequest. */ + public static class Builder { + private String mVersion; + private List mRules; + + public Builder() { + mRules = new ArrayList<>(); + } + + /** + * Set a version string to identify this rule set. This can be retrieved by {@link + * AppIntegrityManager#getCurrentRuleSetVersion()}. + */ + @NonNull + public Builder setVersion(@NonNull String version) { + mVersion = version; + return this; + } + + /** Add the rules to include. */ + @NonNull + public Builder addRules(@NonNull List rules) { + mRules.addAll(rules); + return this; + } + + /** + * Builds a {@link RuleSet}. + * + * @throws IllegalArgumentException if version is null + */ + @NonNull + public RuleSet build() { + checkNotNull(mVersion); + return new RuleSet(mVersion, mRules); + } + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 220fdd2e889d..c3f4384b46d3 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -48,6 +48,7 @@ + diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 74bb2cf6658d..2ede8d1de983 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4179,4 +4179,10 @@ true + + + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 1d5133901064..15de2f463430 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -456,6 +456,7 @@ + diff --git a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java new file mode 100644 index 000000000000..e48f1c3a89c7 --- /dev/null +++ b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2019 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 android.content.integrity; + +import static android.content.integrity.TestUtils.assertExpectException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.integrity.AtomicFormula.BooleanAtomicFormula; +import android.content.integrity.AtomicFormula.IntAtomicFormula; +import android.content.integrity.AtomicFormula.StringAtomicFormula; +import android.os.Parcel; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class AtomicFormulaTest { + + @Test + public void testValidAtomicFormula_stringValue() { + StringAtomicFormula stringAtomicFormula = + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, "com.test.app", /* isHashedValue= */ false); + + assertEquals(AtomicFormula.PACKAGE_NAME, stringAtomicFormula.getKey()); + } + + @Test + public void testValidAtomicFormula_intValue() { + IntAtomicFormula intAtomicFormula = + new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, 1); + + assertEquals(AtomicFormula.VERSION_CODE, intAtomicFormula.getKey()); + } + + @Test + public void testValidAtomicFormula_boolValue() { + BooleanAtomicFormula atomicFormula = + new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true); + + assertEquals(AtomicFormula.PRE_INSTALLED, atomicFormula.getKey()); + } + + @Test + public void testInvalidAtomicFormula_stringValue() { + assertExpectException( + IllegalArgumentException.class, + /* expectedExceptionMessageRegex */ + String.format("Key VERSION_CODE cannot be used with StringAtomicFormula"), + () -> + new StringAtomicFormula( + AtomicFormula.VERSION_CODE, + "test-value", + /* isHashedValue= */ false)); + } + + @Test + public void testInvalidAtomicFormula_intValue() { + assertExpectException( + IllegalArgumentException.class, + /* expectedExceptionMessageRegex */ + String.format("Key PACKAGE_NAME cannot be used with IntAtomicFormula"), + () -> new IntAtomicFormula(AtomicFormula.PACKAGE_NAME, AtomicFormula.EQ, 1)); + } + + @Test + public void testInvalidAtomicFormula_boolValue() { + assertExpectException( + IllegalArgumentException.class, + /* expectedExceptionMessageRegex */ + String.format("Key PACKAGE_NAME cannot be used with BooleanAtomicFormula"), + () -> new BooleanAtomicFormula(AtomicFormula.PACKAGE_NAME, true)); + } + + @Test + public void testIsSatisfiable_string_true() { + StringAtomicFormula stringAtomicFormula = + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, "com.test.app", /* isHashedValue= */ false); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setPackageName("com.test.app").build(); + + assertTrue(stringAtomicFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_string_false() { + StringAtomicFormula stringAtomicFormula = + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, "com.test.app", /* isHashedValue= */ false); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setPackageName("com.foo.bar").build(); + + assertFalse(stringAtomicFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_int_eq_true() { + IntAtomicFormula intAtomicFormula = + new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 0); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setVersionCode(0).build(); + + assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_int_eq_false() { + IntAtomicFormula intAtomicFormula = + new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 0); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setVersionCode(1).build(); + + assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_int_gt_true() { + IntAtomicFormula intAtomicFormula = + new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, 0); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setVersionCode(1).build(); + + assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_int_gt_false() { + IntAtomicFormula intAtomicFormula = + new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, 1); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setVersionCode(0).build(); + + assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_int_ge_true() { + IntAtomicFormula intAtomicFormula = + new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GE, 0); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setVersionCode(1).build(); + + assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_int_ge_false() { + IntAtomicFormula intAtomicFormula = + new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GE, 1); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setVersionCode(0).build(); + + assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_int_lt_true() { + IntAtomicFormula intAtomicFormula = + new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LT, 1); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setVersionCode(0).build(); + + assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_int_lt_false() { + IntAtomicFormula intAtomicFormula = + new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LT, 1); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setVersionCode(2).build(); + + assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_int_le_true() { + IntAtomicFormula intAtomicFormula = + new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, 1); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setVersionCode(0).build(); + + assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_int_le_false() { + IntAtomicFormula intAtomicFormula = + new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, 1); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setVersionCode(2).build(); + + assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_bool_true() { + BooleanAtomicFormula boolFormula = + new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setIsPreInstalled(true).build(); + + assertTrue(boolFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_bool_false() { + BooleanAtomicFormula boolFormula = + new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setIsPreInstalled(false).build(); + + assertFalse(boolFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testParcelUnparcel_string() { + StringAtomicFormula formula = + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, "abc", /* isHashedValue= */ false); + Parcel p = Parcel.obtain(); + formula.writeToParcel(p, 0); + p.setDataPosition(0); + StringAtomicFormula newFormula = StringAtomicFormula.CREATOR.createFromParcel(p); + + assertEquals(formula, newFormula); + } + + @Test + public void testParcelUnparcel_int() { + IntAtomicFormula formula = + new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LT, 1); + Parcel p = Parcel.obtain(); + formula.writeToParcel(p, 0); + p.setDataPosition(0); + IntAtomicFormula newFormula = IntAtomicFormula.CREATOR.createFromParcel(p); + + assertEquals(formula, newFormula); + } + + @Test + public void testParcelUnparcel_bool() { + BooleanAtomicFormula formula = new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true); + Parcel p = Parcel.obtain(); + formula.writeToParcel(p, 0); + p.setDataPosition(0); + BooleanAtomicFormula newFormula = BooleanAtomicFormula.CREATOR.createFromParcel(p); + + assertEquals(formula, newFormula); + } + + @Test + public void testInvalidAtomicFormula_invalidKey() { + assertExpectException( + IllegalArgumentException.class, + /* expectedExceptionMessageRegex */ "Unknown key: -1", + () -> new IntAtomicFormula(/* key= */ -1, AtomicFormula.EQ, 0)); + } + + @Test + public void testInvalidAtomicFormula_invalidOperator() { + assertExpectException( + IllegalArgumentException.class, + /* expectedExceptionMessageRegex */ "Unknown operator: -1", + () -> new IntAtomicFormula(AtomicFormula.VERSION_CODE, /* operator= */ -1, 0)); + } + + /** Returns a builder with all fields filled with some dummy data. */ + private AppInstallMetadata.Builder getAppInstallMetadataBuilder() { + return new AppInstallMetadata.Builder() + .setPackageName("abc") + .setAppCertificate("abc") + .setInstallerCertificate("abc") + .setInstallerName("abc") + .setVersionCode(-1) + .setIsPreInstalled(true); + } +} diff --git a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java new file mode 100644 index 000000000000..927e4dbb5200 --- /dev/null +++ b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2019 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 android.content.integrity; + +import static android.content.integrity.TestUtils.assertExpectException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.os.Parcel; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Arrays; +import java.util.Collections; + +@RunWith(JUnit4.class) +public class CompoundFormulaTest { + + private static final AtomicFormula ATOMIC_FORMULA_1 = + new AtomicFormula.StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, "test1", /* isHashedValue= */ false); + private static final AtomicFormula ATOMIC_FORMULA_2 = + new AtomicFormula.IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1); + + @Test + public void testValidCompoundFormula() { + CompoundFormula compoundFormula = + new CompoundFormula( + CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); + + assertEquals(CompoundFormula.AND, compoundFormula.getConnector()); + assertEquals( + Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2), compoundFormula.getFormulas()); + } + + @Test + public void testValidateAuxiliaryFormula_binaryConnectors() { + assertExpectException( + IllegalArgumentException.class, + /* expectedExceptionMessageRegex */ + "Connector AND must have at least 2 formulas", + () -> + new CompoundFormula( + CompoundFormula.AND, Collections.singletonList(ATOMIC_FORMULA_1))); + } + + @Test + public void testValidateAuxiliaryFormula_unaryConnectors() { + assertExpectException( + IllegalArgumentException.class, + /* expectedExceptionMessageRegex */ + "Connector NOT must have 1 formula only", + () -> + new CompoundFormula( + CompoundFormula.NOT, + Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2))); + } + + @Test + public void testIsSatisfiable_notFalse_true() { + CompoundFormula compoundFormula = + new CompoundFormula(CompoundFormula.NOT, Arrays.asList(ATOMIC_FORMULA_1)); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setPackageName("test2").build(); + // validate assumptions about the metadata + assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); + + assertTrue(compoundFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_notTrue_false() { + CompoundFormula compoundFormula = + new CompoundFormula(CompoundFormula.NOT, Arrays.asList(ATOMIC_FORMULA_1)); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setPackageName("test1").build(); + // validate assumptions about the metadata + assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); + + assertFalse(compoundFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_trueAndTrue_true() { + CompoundFormula compoundFormula = + new CompoundFormula( + CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(1).build(); + // validate assumptions about the metadata + assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); + assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); + + assertTrue(compoundFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_trueAndFalse_false() { + CompoundFormula compoundFormula = + new CompoundFormula( + CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(2).build(); + // validate assumptions about the metadata + assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); + assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); + + assertFalse(compoundFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_falseAndTrue_false() { + CompoundFormula compoundFormula = + new CompoundFormula( + CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(1).build(); + // validate assumptions about the metadata + assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); + assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); + + assertFalse(compoundFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_falseAndFalse_false() { + CompoundFormula compoundFormula = + new CompoundFormula( + CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(2).build(); + // validate assumptions about the metadata + assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); + assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); + + assertFalse(compoundFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_trueOrTrue_true() { + CompoundFormula compoundFormula = + new CompoundFormula( + CompoundFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(1).build(); + // validate assumptions about the metadata + assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); + assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); + + assertTrue(compoundFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_trueOrFalse_true() { + CompoundFormula compoundFormula = + new CompoundFormula( + CompoundFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(2).build(); + // validate assumptions about the metadata + assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); + assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); + + assertTrue(compoundFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_falseOrTrue_true() { + CompoundFormula compoundFormula = + new CompoundFormula( + CompoundFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(1).build(); + // validate assumptions about the metadata + assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); + assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); + + assertTrue(compoundFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testIsSatisfiable_falseOrFalse_false() { + CompoundFormula compoundFormula = + new CompoundFormula( + CompoundFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); + AppInstallMetadata appInstallMetadata = + getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(2).build(); + // validate assumptions about the metadata + assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); + assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); + + assertFalse(compoundFormula.isSatisfied(appInstallMetadata)); + } + + @Test + public void testParcelUnparcel() { + CompoundFormula formula = + new CompoundFormula( + CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_2, ATOMIC_FORMULA_1)); + Parcel p = Parcel.obtain(); + formula.writeToParcel(p, 0); + p.setDataPosition(0); + CompoundFormula newFormula = CompoundFormula.CREATOR.createFromParcel(p); + + assertEquals(formula, newFormula); + } + + @Test + public void testInvalidCompoundFormula_invalidConnector() { + assertExpectException( + IllegalArgumentException.class, + /* expectedExceptionMessageRegex */ "Unknown connector: -1", + () -> + new CompoundFormula( + /* connector= */ -1, + Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2))); + } + + /** Returns a builder with all fields filled with some dummy data. */ + private AppInstallMetadata.Builder getAppInstallMetadataBuilder() { + return new AppInstallMetadata.Builder() + .setPackageName("abc") + .setAppCertificate("abc") + .setInstallerCertificate("abc") + .setInstallerName("abc") + .setVersionCode(-1) + .setIsPreInstalled(true); + } +} diff --git a/core/tests/coretests/src/android/content/integrity/RuleTest.java b/core/tests/coretests/src/android/content/integrity/RuleTest.java new file mode 100644 index 000000000000..19e74e6b93cc --- /dev/null +++ b/core/tests/coretests/src/android/content/integrity/RuleTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2019 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 android.content.integrity; + +import static android.content.integrity.TestUtils.assertExpectException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.os.Parcel; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Arrays; + +@RunWith(JUnit4.class) +public class RuleTest { + + private static final @Rule.Effect int DENY_EFFECT = Rule.DENY; + private static final String PACKAGE_NAME = "com.test.app"; + private static final String APP_CERTIFICATE = "test_cert"; + private static final Formula PACKAGE_NAME_ATOMIC_FORMULA = + new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME, + /* isHashedValue= */ false); + private static final Formula APP_CERTIFICATE_ATOMIC_FORMULA = + new AtomicFormula.StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, APP_CERTIFICATE, + /* isHashedValue= */ false); + + @Test + public void testValidRule() { + Rule validRule = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT); + + assertEquals(PACKAGE_NAME_ATOMIC_FORMULA, validRule.getFormula()); + assertEquals(DENY_EFFECT, validRule.getEffect()); + } + + @Test + public void testInvalidRule_invalidFormula() { + assertExpectException( + NullPointerException.class, + /* expectedExceptionMessageRegex */ null, + () -> new Rule(null, DENY_EFFECT)); + } + + @Test + public void testToString() { + CompoundFormula compoundFormula = + new CompoundFormula( + CompoundFormula.AND, + Arrays.asList(PACKAGE_NAME_ATOMIC_FORMULA, APP_CERTIFICATE_ATOMIC_FORMULA)); + Rule rule = new Rule(compoundFormula, Rule.DENY); + + assertEquals( + String.format( + "Rule: (PACKAGE_NAME EQ %s) AND (APP_CERTIFICATE EQ %s), DENY", + PACKAGE_NAME, APP_CERTIFICATE), + rule.toString()); + } + + @Test + public void testEquals_trueCase() { + Rule rule1 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT); + Rule rule2 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT); + + assertEquals(rule1, rule2); + } + + @Test + public void testEquals_falseCase() { + Rule rule1 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT); + Rule rule2 = new Rule(APP_CERTIFICATE_ATOMIC_FORMULA, DENY_EFFECT); + + assertNotEquals(rule1, rule2); + } + + @Test + public void testParcelUnparcel() { + Rule rule = + new Rule( + new CompoundFormula( + CompoundFormula.AND, + Arrays.asList( + APP_CERTIFICATE_ATOMIC_FORMULA, + new CompoundFormula( + CompoundFormula.NOT, + Arrays.asList(PACKAGE_NAME_ATOMIC_FORMULA)))), + Rule.DENY); + Parcel p = Parcel.obtain(); + rule.writeToParcel(p, 0); + p.setDataPosition(0); + Rule newRule = Rule.CREATOR.createFromParcel(p); + + assertEquals(newRule, rule); + } + + @Test + public void testInvalidRule_invalidEffect() { + assertExpectException( + IllegalArgumentException.class, + /* expectedExceptionMessageRegex */ "Unknown effect: -1", + () -> new Rule(PACKAGE_NAME_ATOMIC_FORMULA, /* effect= */ -1)); + } +} diff --git a/core/tests/coretests/src/android/content/integrity/TEST_MAPPING b/core/tests/coretests/src/android/content/integrity/TEST_MAPPING new file mode 100644 index 000000000000..2920716f5d5d --- /dev/null +++ b/core/tests/coretests/src/android/content/integrity/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.content.integrity." + } + ] + } + ] +} diff --git a/core/tests/coretests/src/android/content/integrity/TestUtils.java b/core/tests/coretests/src/android/content/integrity/TestUtils.java new file mode 100644 index 000000000000..af984cf318f5 --- /dev/null +++ b/core/tests/coretests/src/android/content/integrity/TestUtils.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 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 android.content.integrity; + +import android.test.MoreAsserts; + +import junit.framework.Assert; + +/** Helper methods used in tests. */ +class TestUtils { + private TestUtils() {} + + public interface ExceptionRunnable { + void run() throws Exception; + } + + public static void assertExpectException( + Class expectedExceptionType, + String expectedExceptionMessageRegex, + ExceptionRunnable r) { + try { + r.run(); + } catch (Throwable e) { + Assert.assertTrue( + "Expected exception type was " + + expectedExceptionType.getName() + + " but caught " + + e.getClass().getName(), + expectedExceptionType.isAssignableFrom(e.getClass())); + if (expectedExceptionMessageRegex != null) { + MoreAsserts.assertContainsRegex(expectedExceptionMessageRegex, e.getMessage()); + } + return; // Pass. + } + Assert.fail( + "Expected exception type " + expectedExceptionType.getName() + " was not thrown"); + } +} diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java index f0bb192e3b61..b8202b6c0dc6 100644 --- a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java +++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java @@ -16,9 +16,10 @@ package com.android.server.integrity.engine; -import com.android.server.integrity.model.AppInstallMetadata; +import android.content.integrity.AppInstallMetadata; +import android.content.integrity.Rule; + import com.android.server.integrity.model.IntegrityCheckResult; -import com.android.server.integrity.model.Rule; import java.util.ArrayList; import java.util.List; @@ -26,8 +27,8 @@ import java.util.List; /** * The engine used to evaluate rules against app installs. * - *

Every app install is evaluated against rules (pushed by the verifier) by the evaluation - * engine to allow/block that install. + *

Every app install is evaluated against rules (pushed by the verifier) by the evaluation engine + * to allow/block that install. */ public final class RuleEvaluationEngine { private static final String TAG = "RuleEvaluation"; @@ -36,9 +37,7 @@ public final class RuleEvaluationEngine { // installs against rules. private static RuleEvaluationEngine sRuleEvaluationEngine; - /** - * Provide a singleton instance of the rule evaluation engine. - */ + /** Provide a singleton instance of the rule evaluation engine. */ public static synchronized RuleEvaluationEngine getRuleEvaluationEngine() { if (sRuleEvaluationEngine == null) { return new RuleEvaluationEngine(); @@ -50,7 +49,7 @@ public final class RuleEvaluationEngine { * Load, and match the list of rules against an app install metadata. * * @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules - * against. + * against. * @return result of the integrity check */ public IntegrityCheckResult evaluate(AppInstallMetadata appInstallMetadata) { diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java index 7deae466ac3f..ee51d4f87848 100644 --- a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java +++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java @@ -16,18 +16,18 @@ package com.android.server.integrity.engine; -import static com.android.server.integrity.model.Rule.DENY; -import static com.android.server.integrity.model.Rule.FORCE_ALLOW; +import static android.content.integrity.Rule.DENY; +import static android.content.integrity.Rule.FORCE_ALLOW; import android.annotation.NonNull; +import android.content.integrity.AppInstallMetadata; +import android.content.integrity.AtomicFormula; +import android.content.integrity.CompoundFormula; +import android.content.integrity.Formula; +import android.content.integrity.Rule; import android.util.Slog; -import com.android.server.integrity.model.AppInstallMetadata; -import com.android.server.integrity.model.AtomicFormula; -import com.android.server.integrity.model.Formula; import com.android.server.integrity.model.IntegrityCheckResult; -import com.android.server.integrity.model.OpenFormula; -import com.android.server.integrity.model.Rule; import java.util.ArrayList; import java.util.List; @@ -89,17 +89,17 @@ final class RuleEvaluator { if (isAtomicFormula(formula)) { return true; } - OpenFormula openFormula = (OpenFormula) formula; - return openFormula.getConnector() == OpenFormula.AND - && openFormula.getFormulas().stream().allMatch(RuleEvaluator::isAtomicFormula); + CompoundFormula compoundFormula = (CompoundFormula) formula; + return compoundFormula.getConnector() == CompoundFormula.AND + && compoundFormula.getFormulas().stream().allMatch(RuleEvaluator::isAtomicFormula); } private static boolean isAtomicFormula(Formula formula) { if (formula instanceof AtomicFormula) { return true; } - OpenFormula openFormula = (OpenFormula) formula; - return openFormula.getConnector() == OpenFormula.NOT - && openFormula.getFormulas().get(0) instanceof AtomicFormula; + CompoundFormula compoundFormula = (CompoundFormula) formula; + return compoundFormula.getConnector() == CompoundFormula.NOT + && compoundFormula.getFormulas().get(0) instanceof AtomicFormula; } } diff --git a/services/core/java/com/android/server/integrity/engine/RuleLoader.java b/services/core/java/com/android/server/integrity/engine/RuleLoader.java index af24d7a21dc8..4ba2bfb00d05 100644 --- a/services/core/java/com/android/server/integrity/engine/RuleLoader.java +++ b/services/core/java/com/android/server/integrity/engine/RuleLoader.java @@ -16,7 +16,7 @@ package com.android.server.integrity.engine; -import com.android.server.integrity.model.Rule; +import android.content.integrity.Rule; import java.util.ArrayList; import java.util.List; diff --git a/services/core/java/com/android/server/integrity/model/AppInstallMetadata.java b/services/core/java/com/android/server/integrity/model/AppInstallMetadata.java deleted file mode 100644 index dfc373bb97dc..000000000000 --- a/services/core/java/com/android/server/integrity/model/AppInstallMetadata.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.model; - -import static com.android.internal.util.Preconditions.checkArgument; -import static com.android.internal.util.Preconditions.checkNotNull; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; - -import com.android.internal.annotations.VisibleForTesting; - -/** - * The app install metadata. - * - *

The integrity component retrieves metadata for app installs from package manager, passing it - * to the rule evaluation engine to evaluate the metadata against the rules. - * - *

Instances of this class are immutable. - * - * @hide - */ -@SystemApi -@VisibleForTesting -public final class AppInstallMetadata { - private final String mPackageName; - // Raw string encoding for the SHA-256 hash of the certificate of the app. - private final String mAppCertificate; - private final String mInstallerName; - // Raw string encoding for the SHA-256 hash of the certificate of the installer. - private final String mInstallerCertificate; - private final int mVersionCode; - private final boolean mIsPreInstalled; - - private AppInstallMetadata(Builder builder) { - this.mPackageName = builder.mPackageName; - this.mAppCertificate = builder.mAppCertificate; - this.mInstallerName = builder.mInstallerName; - this.mInstallerCertificate = builder.mInstallerCertificate; - this.mVersionCode = builder.mVersionCode; - this.mIsPreInstalled = builder.mIsPreInstalled; - } - - @NonNull - public String getPackageName() { - return mPackageName; - } - - @NonNull - public String getAppCertificate() { - return mAppCertificate; - } - - @Nullable - public String getInstallerName() { - return mInstallerName; - } - - @Nullable - public String getInstallerCertificate() { - return mInstallerCertificate; - } - - /** @see AppInstallMetadata.Builder#setVersionCode(int) */ - public int getVersionCode() { - return mVersionCode; - } - - /** @see AppInstallMetadata.Builder#setIsPreInstalled(boolean) */ - public boolean isPreInstalled() { - return mIsPreInstalled; - } - - /** Builder class for constructing {@link AppInstallMetadata} objects. */ - public static final class Builder { - private String mPackageName; - private String mAppCertificate; - private String mInstallerName; - private String mInstallerCertificate; - private int mVersionCode; - private boolean mIsPreInstalled; - - /** - * Set package name of the app to be installed. - * - * @see AppInstallMetadata#getPackageName() - */ - @NonNull - public Builder setPackageName(@NonNull String packageName) { - this.mPackageName = checkNotNull(packageName); - return this; - } - - /** - * Set certificate of the app to be installed. - * - *

It is represented as the raw string encoding for the SHA-256 hash of the certificate - * of the app. - * - * @see AppInstallMetadata#getAppCertificate() - */ - @NonNull - public Builder setAppCertificate(@NonNull String appCertificate) { - this.mAppCertificate = checkNotNull(appCertificate); - return this; - } - - /** - * Set name of the installer installing the app. - * - * @see AppInstallMetadata#getInstallerName() - */ - @NonNull - public Builder setInstallerName(@NonNull String installerName) { - this.mInstallerName = checkNotNull(installerName); - return this; - } - - /** - * Set certificate of the installer installing the app. - * - *

It is represented as the raw string encoding for the SHA-256 hash of the certificate - * of the installer. - * - * @see AppInstallMetadata#getInstallerCertificate() - */ - @NonNull - public Builder setInstallerCertificate(@NonNull String installerCertificate) { - this.mInstallerCertificate = checkNotNull(installerCertificate); - return this; - } - - /** - * Set version code of the app to be installed. - * - * @see AppInstallMetadata#getVersionCode() - */ - @NonNull - public Builder setVersionCode(int versionCode) { - this.mVersionCode = versionCode; - return this; - } - - /** - * Set whether the app is pre-installed on the device or not. - * - * @see AppInstallMetadata#isPreInstalled() - */ - @NonNull - public Builder setIsPreInstalled(boolean isPreInstalled) { - this.mIsPreInstalled = isPreInstalled; - return this; - } - - /** - * Build {@link AppInstallMetadata}. - * - * @throws IllegalArgumentException if package name or app certificate is null - */ - @NonNull - public AppInstallMetadata build() { - checkArgument(mPackageName != null); - checkArgument(mAppCertificate != null); - return new AppInstallMetadata(this); - } - } -} diff --git a/services/core/java/com/android/server/integrity/model/AtomicFormula.java b/services/core/java/com/android/server/integrity/model/AtomicFormula.java deleted file mode 100644 index a16f6b9b5970..000000000000 --- a/services/core/java/com/android/server/integrity/model/AtomicFormula.java +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.model; - -import static com.android.internal.util.Preconditions.checkArgument; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Slog; - -import com.android.internal.annotations.VisibleForTesting; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -/** - * Represents a simple formula consisting of an app install metadata field and a value. - * - *

Instances of this class are immutable. - * - * @hide - */ -@SystemApi -@VisibleForTesting -public abstract class AtomicFormula implements Formula { - - private static final String TAG = "AtomicFormula"; - - @IntDef( - value = { - PACKAGE_NAME, - APP_CERTIFICATE, - INSTALLER_NAME, - INSTALLER_CERTIFICATE, - VERSION_CODE, - PRE_INSTALLED, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Key {} - - @IntDef(value = {EQ, LT, LE, GT, GE}) - @Retention(RetentionPolicy.SOURCE) - public @interface Operator {} - - /** - * Package name of the app. - * - *

Can only be used in {@link StringAtomicFormula}. - */ - public static final int PACKAGE_NAME = 0; - - /** - * SHA-256 of the app certificate of the app. - * - *

Can only be used in {@link StringAtomicFormula}. - */ - public static final int APP_CERTIFICATE = 1; - - /** - * Package name of the installer. Will be empty string if installed by the system (e.g., adb). - * - *

Can only be used in {@link StringAtomicFormula}. - */ - public static final int INSTALLER_NAME = 2; - - /** - * SHA-256 of the cert of the installer. Will be empty string if installed by the system (e.g., - * adb). - * - *

Can only be used in {@link StringAtomicFormula}. - */ - public static final int INSTALLER_CERTIFICATE = 3; - - /** - * Version code of the app. - * - *

Can only be used in {@link IntAtomicFormula}. - */ - public static final int VERSION_CODE = 4; - - /** - * If the app is pre-installed on the device. - * - *

Can only be used in {@link BooleanAtomicFormula}. - */ - public static final int PRE_INSTALLED = 5; - - public static final int EQ = 0; - public static final int LT = 1; - public static final int LE = 2; - public static final int GT = 3; - public static final int GE = 4; - - private final @Key int mKey; - - public AtomicFormula(@Key int key) { - checkArgument(isValidKey(key), String.format("Unknown key: %d", key)); - mKey = key; - } - - /** An {@link AtomicFormula} with an key and int value. */ - public static final class IntAtomicFormula extends AtomicFormula implements Parcelable { - private final int mValue; - private final @Operator int mOperator; - - /** - * Constructs a new {@link IntAtomicFormula}. - * - *

This formula will hold if and only if the corresponding information of an install - * specified by {@code key} is of the correct relationship to {@code value} as specified by - * {@code operator}. - * - * @throws IllegalArgumentException if {@code key} cannot be used with integer value - */ - public IntAtomicFormula(@Key int key, @Operator int operator, int value) { - super(key); - checkArgument( - key == VERSION_CODE, - String.format("Key %s cannot be used with IntAtomicFormula", keyToString(key))); - checkArgument(isValidOperator(operator), - String.format("Unknown operator: %d", operator)); - mOperator = operator; - mValue = value; - } - - IntAtomicFormula(Parcel in) { - super(in.readInt()); - mValue = in.readInt(); - mOperator = in.readInt(); - } - - @NonNull - public static final Creator CREATOR = - new Creator() { - @Override - public IntAtomicFormula createFromParcel(Parcel in) { - return new IntAtomicFormula(in); - } - - @Override - public IntAtomicFormula[] newArray(int size) { - return new IntAtomicFormula[size]; - } - }; - - @Override - public boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) { - int metadataValue = getMetadataValueByKey(appInstallMetadata); - switch (mOperator) { - case EQ: - return metadataValue == mValue; - case LE: - return metadataValue <= mValue; - case LT: - return metadataValue < mValue; - case GE: - return metadataValue >= mValue; - case GT: - return metadataValue > mValue; - default: - Slog.i(TAG, String.format("Unexpected operator %d", mOperator)); - return false; - } - } - - @Override - public int getTag() { - return Formula.INT_ATOMIC_FORMULA_TAG; - } - - @Override - public String toString() { - return String.format( - "(%s %s %s)", keyToString(getKey()), operatorToString(mOperator), mValue); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - IntAtomicFormula that = (IntAtomicFormula) o; - return getKey() == that.getKey() - && mValue == that.mValue - && mOperator == that.mOperator; - } - - @Override - public int hashCode() { - return Objects.hash(getKey(), mOperator, mValue); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(getKey()); - dest.writeInt(mValue); - dest.writeInt(mOperator); - } - - public int getValue() { - return mValue; - } - - public int getOperator() { - return mOperator; - } - - private int getMetadataValueByKey(AppInstallMetadata appInstallMetadata) { - switch (getKey()) { - case VERSION_CODE: - return appInstallMetadata.getVersionCode(); - default: - throw new IllegalStateException( - "Unexpected key in IntAtomicFormula" + getKey()); - } - } - - private static boolean isValidOperator(int operator) { - return operator == EQ - || operator == LT - || operator == LE - || operator == GT - || operator == GE; - } - } - - /** An {@link AtomicFormula} with a key and string value. */ - public static final class StringAtomicFormula extends AtomicFormula implements Parcelable { - private final String mValue; - // Indicates whether the value is the actual value or the hashed value. - private final boolean mIsHashedValue; - - /** - * Constructs a new {@link StringAtomicFormula}. - * - *

This formula will hold if and only if the corresponding information of an install - * specified by {@code key} equals {@code value}. - * - * @throws IllegalArgumentException if {@code key} cannot be used with string value - */ - public StringAtomicFormula(@Key int key, @NonNull String value, boolean isHashedValue) { - super(key); - mIsHashedValue = isHashedValue; - checkArgument( - key == PACKAGE_NAME - || key == APP_CERTIFICATE - || key == INSTALLER_CERTIFICATE - || key == INSTALLER_NAME, - String.format( - "Key %s cannot be used with StringAtomicFormula", keyToString(key))); - mValue = value; - } - - StringAtomicFormula(Parcel in) { - super(in.readInt()); - mValue = in.readStringNoHelper(); - mIsHashedValue = in.readByte() != 0; - } - - @NonNull - public static final Creator CREATOR = - new Creator() { - @Override - public StringAtomicFormula createFromParcel(Parcel in) { - return new StringAtomicFormula(in); - } - - @Override - public StringAtomicFormula[] newArray(int size) { - return new StringAtomicFormula[size]; - } - }; - - @Override - public boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) { - String metadataValue = getMetadataValueByKey(appInstallMetadata); - return metadataValue.equals(mValue); - } - - @Override - public int getTag() { - return Formula.STRING_ATOMIC_FORMULA_TAG; - } - - @Override - public String toString() { - return String.format("(%s %s %s)", keyToString(getKey()), operatorToString(EQ), mValue); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - StringAtomicFormula that = (StringAtomicFormula) o; - return getKey() == that.getKey() && Objects.equals(mValue, that.mValue); - } - - @Override - public int hashCode() { - return Objects.hash(getKey(), mValue); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(getKey()); - dest.writeStringNoHelper(mValue); - dest.writeByte((byte) (mIsHashedValue ? 1 : 0)); - } - - @NonNull - public String getValue() { - return mValue; - } - - public boolean getIsHashedValue() { - return mIsHashedValue; - } - - private String getMetadataValueByKey(AppInstallMetadata appInstallMetadata) { - switch (getKey()) { - case PACKAGE_NAME: - return appInstallMetadata.getPackageName(); - case APP_CERTIFICATE: - return appInstallMetadata.getAppCertificate(); - case INSTALLER_CERTIFICATE: - return appInstallMetadata.getInstallerCertificate(); - case INSTALLER_NAME: - return appInstallMetadata.getInstallerName(); - default: - throw new IllegalStateException( - "Unexpected key in StringAtomicFormula: " + getKey()); - } - } - } - - /** An {@link AtomicFormula} with a key and boolean value. */ - public static final class BooleanAtomicFormula extends AtomicFormula implements Parcelable { - private final boolean mValue; - - /** - * Constructs a new {@link BooleanAtomicFormula}. - * - *

This formula will hold if and only if the corresponding information of an install - * specified by {@code key} equals {@code value}. - * - * @throws IllegalArgumentException if {@code key} cannot be used with boolean value - */ - public BooleanAtomicFormula(@Key int key, boolean value) { - super(key); - checkArgument( - key == PRE_INSTALLED, - String.format( - "Key %s cannot be used with BooleanAtomicFormula", keyToString(key))); - mValue = value; - } - - BooleanAtomicFormula(Parcel in) { - super(in.readInt()); - mValue = in.readByte() != 0; - } - - @NonNull - public static final Creator CREATOR = - new Creator() { - @Override - public BooleanAtomicFormula createFromParcel(Parcel in) { - return new BooleanAtomicFormula(in); - } - - @Override - public BooleanAtomicFormula[] newArray(int size) { - return new BooleanAtomicFormula[size]; - } - }; - - @Override - public boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) { - boolean metadataValue = getMetadataValueByKey(appInstallMetadata); - return metadataValue == mValue; - } - - @Override - public int getTag() { - return Formula.BOOLEAN_ATOMIC_FORMULA_TAG; - } - - @Override - public String toString() { - return String.format("(%s %s %s)", keyToString(getKey()), operatorToString(EQ), mValue); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - BooleanAtomicFormula that = (BooleanAtomicFormula) o; - return getKey() == that.getKey() && mValue == that.mValue; - } - - @Override - public int hashCode() { - return Objects.hash(getKey(), mValue); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(getKey()); - dest.writeByte((byte) (mValue ? 1 : 0)); - } - - public boolean getValue() { - return mValue; - } - - private boolean getMetadataValueByKey(AppInstallMetadata appInstallMetadata) { - switch (getKey()) { - case PRE_INSTALLED: - return appInstallMetadata.isPreInstalled(); - default: - throw new IllegalStateException( - "Unexpected key in BooleanAtomicFormula: " + getKey()); - } - } - } - - public int getKey() { - return mKey; - } - - static String keyToString(int key) { - switch (key) { - case PACKAGE_NAME: - return "PACKAGE_NAME"; - case APP_CERTIFICATE: - return "APP_CERTIFICATE"; - case VERSION_CODE: - return "VERSION_CODE"; - case INSTALLER_NAME: - return "INSTALLER_NAME"; - case INSTALLER_CERTIFICATE: - return "INSTALLER_CERTIFICATE"; - case PRE_INSTALLED: - return "PRE_INSTALLED"; - default: - throw new IllegalArgumentException("Unknown key " + key); - } - } - - static String operatorToString(int op) { - switch (op) { - case EQ: - return "EQ"; - case LT: - return "LT"; - case LE: - return "LE"; - case GT: - return "GT"; - case GE: - return "GE"; - default: - throw new IllegalArgumentException("Unknown operator " + op); - } - } - - private static boolean isValidKey(int key) { - return key == PACKAGE_NAME - || key == APP_CERTIFICATE - || key == VERSION_CODE - || key == INSTALLER_NAME - || key == INSTALLER_CERTIFICATE - || key == PRE_INSTALLED; - } -} diff --git a/services/core/java/com/android/server/integrity/model/Formula.java b/services/core/java/com/android/server/integrity/model/Formula.java deleted file mode 100644 index 67698277800f..000000000000 --- a/services/core/java/com/android/server/integrity/model/Formula.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.model; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.integrity.model.AtomicFormula.BooleanAtomicFormula; -import com.android.server.integrity.model.AtomicFormula.IntAtomicFormula; -import com.android.server.integrity.model.AtomicFormula.StringAtomicFormula; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Represents a rule logic/content. - * - * @hide - */ -@SystemApi -@VisibleForTesting -public interface Formula { - @IntDef( - value = { - OPEN_FORMULA_TAG, - STRING_ATOMIC_FORMULA_TAG, - INT_ATOMIC_FORMULA_TAG, - BOOLEAN_ATOMIC_FORMULA_TAG - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Tag {} - - int OPEN_FORMULA_TAG = 0; - int STRING_ATOMIC_FORMULA_TAG = 1; - int INT_ATOMIC_FORMULA_TAG = 2; - int BOOLEAN_ATOMIC_FORMULA_TAG = 3; - - /** - * Returns if this formula can be satisfied by substituting the corresponding information of - * {@code appInstallMetadata} into the formula. - */ - boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata); - - /** Returns the tag that identifies the current class. */ - @Tag int getTag(); - - /** - * Write a {@link Formula} to {@link android.os.Parcel}. - * - *

This helper method is needed because non-final class/interface are not allowed to be - * {@link Parcelable}. - * - * @throws IllegalArgumentException if {@link Formula} is not a recognized subclass - */ - static void writeToParcel(@NonNull Formula formula, @NonNull Parcel dest, int flags) { - dest.writeInt(formula.getTag()); - ((Parcelable) formula).writeToParcel(dest, flags); - } - - /** - * Read a {@link Formula} from a {@link android.os.Parcel}. - * - *

We need this (hacky) helper method because non-final class/interface cannot be {@link - * Parcelable} (api lint error). - * - * @throws IllegalArgumentException if the parcel cannot be parsed - */ - @NonNull - static Formula readFromParcel(@NonNull Parcel in) { - int tag = in.readInt(); - switch (tag) { - case OPEN_FORMULA_TAG: - return OpenFormula.CREATOR.createFromParcel(in); - case STRING_ATOMIC_FORMULA_TAG: - return StringAtomicFormula.CREATOR.createFromParcel(in); - case INT_ATOMIC_FORMULA_TAG: - return IntAtomicFormula.CREATOR.createFromParcel(in); - case BOOLEAN_ATOMIC_FORMULA_TAG: - return BooleanAtomicFormula.CREATOR.createFromParcel(in); - default: - throw new IllegalArgumentException("Unknown formula tag " + tag); - } - } -} diff --git a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java index ef0751d329d0..b3cb31ac8cb1 100644 --- a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java +++ b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java @@ -17,6 +17,7 @@ package com.android.server.integrity.model; import android.annotation.Nullable; +import android.content.integrity.Rule; /** * A class encapsulating the result from the evaluation engine after evaluating rules against app diff --git a/services/core/java/com/android/server/integrity/model/OpenFormula.java b/services/core/java/com/android/server/integrity/model/OpenFormula.java deleted file mode 100644 index f7ea9208f7fe..000000000000 --- a/services/core/java/com/android/server/integrity/model/OpenFormula.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.model; - -import static com.android.internal.util.Preconditions.checkArgument; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Slog; - -import com.android.internal.annotations.VisibleForTesting; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -/** - * Represents a complex formula consisting of other simple and complex formulas. - * - *

Instances of this class are immutable. - * - * @hide - */ -@SystemApi -@VisibleForTesting -public final class OpenFormula implements Formula, Parcelable { - private static final String TAG = "OpenFormula"; - - @IntDef( - value = { - AND, OR, NOT, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Connector {} - - /** Boolean AND operator. */ - public static final int AND = 0; - - /** Boolean OR operator. */ - public static final int OR = 1; - - /** Boolean NOT operator. */ - public static final int NOT = 2; - - private final @Connector int mConnector; - private final @NonNull List mFormulas; - - @NonNull - public static final Creator CREATOR = - new Creator() { - @Override - public OpenFormula createFromParcel(Parcel in) { - return new OpenFormula(in); - } - - @Override - public OpenFormula[] newArray(int size) { - return new OpenFormula[size]; - } - }; - - /** - * Create a new formula from operator and operands. - * - * @throws IllegalArgumentException if the number of operands is not matching the requirements - * for that operator (at least 2 for {@link #AND} and {@link #OR}, 1 for {@link #NOT}). - */ - public OpenFormula(@Connector int connector, @NonNull List formulas) { - checkArgument(isValidConnector(connector), - String.format("Unknown connector: %d", connector)); - validateFormulas(connector, formulas); - this.mConnector = connector; - this.mFormulas = Collections.unmodifiableList(formulas); - } - - OpenFormula(Parcel in) { - mConnector = in.readInt(); - int length = in.readInt(); - checkArgument(length >= 0, "Must have non-negative length. Got " + length); - mFormulas = new ArrayList<>(length); - for (int i = 0; i < length; i++) { - mFormulas.add(Formula.readFromParcel(in)); - } - validateFormulas(mConnector, mFormulas); - } - - public @Connector int getConnector() { - return mConnector; - } - - @NonNull - public List getFormulas() { - return mFormulas; - } - - @Override - public boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) { - switch (mConnector) { - case NOT: - return !mFormulas.get(0).isSatisfied(appInstallMetadata); - case AND: - return mFormulas.stream() - .allMatch(formula -> formula.isSatisfied(appInstallMetadata)); - case OR: - return mFormulas.stream() - .anyMatch(formula -> formula.isSatisfied(appInstallMetadata)); - default: - Slog.i(TAG, "Unknown connector " + mConnector); - return false; - } - } - - @Override - public int getTag() { - return Formula.OPEN_FORMULA_TAG; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - if (mFormulas.size() == 1) { - sb.append(String.format("%s ", connectorToString(mConnector))); - sb.append(mFormulas.get(0).toString()); - } else { - for (int i = 0; i < mFormulas.size(); i++) { - if (i > 0) { - sb.append(String.format(" %s ", connectorToString(mConnector))); - } - sb.append(mFormulas.get(i).toString()); - } - } - return sb.toString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - OpenFormula that = (OpenFormula) o; - return mConnector == that.mConnector && mFormulas.equals(that.mFormulas); - } - - @Override - public int hashCode() { - return Objects.hash(mConnector, mFormulas); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mConnector); - dest.writeInt(mFormulas.size()); - for (Formula formula : mFormulas) { - Formula.writeToParcel(formula, dest, flags); - } - } - - private static void validateFormulas(@Connector int connector, List formulas) { - switch (connector) { - case AND: - case OR: - checkArgument( - formulas.size() >= 2, - String.format( - "Connector %s must have at least 2 formulas", - connectorToString(connector))); - break; - case NOT: - checkArgument( - formulas.size() == 1, - String.format( - "Connector %s must have 1 formula only", - connectorToString(connector))); - break; - } - } - - private static String connectorToString(int connector) { - switch (connector) { - case AND: - return "AND"; - case OR: - return "OR"; - case NOT: - return "NOT"; - default: - throw new IllegalArgumentException("Unknown connector " + connector); - } - } - - private static boolean isValidConnector(int connector) { - return connector == AND - || connector == OR - || connector == NOT; - } -} diff --git a/services/core/java/com/android/server/integrity/model/Rule.java b/services/core/java/com/android/server/integrity/model/Rule.java deleted file mode 100644 index 3ad876292ab4..000000000000 --- a/services/core/java/com/android/server/integrity/model/Rule.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.model; - -import static com.android.internal.util.Preconditions.checkArgument; -import static com.android.internal.util.Preconditions.checkNotNull; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; - -import com.android.internal.annotations.VisibleForTesting; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -/** - * Represent rules to be used in the rule evaluation engine to match against app installs. - * - *

Instances of this class are immutable. - * - * @hide - */ -@SystemApi -@VisibleForTesting -public final class Rule implements Parcelable { - - @IntDef( - value = { - DENY, - FORCE_ALLOW, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Effect {} - - /** If this rule matches the install, the install should be denied. */ - public static final int DENY = 0; - - /** - * If this rule matches the install, the install will be allowed regardless of other matched - * rules. - */ - public static final int FORCE_ALLOW = 1; - - private final @NonNull Formula mFormula; - private final @Effect int mEffect; - - public Rule(@NonNull Formula formula, @Effect int effect) { - checkArgument(isValidEffect(effect), String.format("Unknown effect: %d", effect)); - this.mFormula = checkNotNull(formula); - this.mEffect = effect; - } - - Rule(Parcel in) { - mFormula = Formula.readFromParcel(in); - mEffect = in.readInt(); - } - - @NonNull - public static final Creator CREATOR = - new Creator() { - @Override - public Rule createFromParcel(Parcel in) { - return new Rule(in); - } - - @Override - public Rule[] newArray(int size) { - return new Rule[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - Formula.writeToParcel(mFormula, dest, flags); - dest.writeInt(mEffect); - } - - @NonNull - public Formula getFormula() { - return mFormula; - } - - public @Effect int getEffect() { - return mEffect; - } - - @Override - public String toString() { - return String.format("Rule: %s, %s", mFormula, effectToString(mEffect)); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Rule that = (Rule) o; - return mEffect == that.mEffect && Objects.equals(mFormula, that.mFormula); - } - - @Override - public int hashCode() { - return Objects.hash(mFormula, mEffect); - } - - private static String effectToString(int effect) { - switch (effect) { - case DENY: - return "DENY"; - case FORCE_ALLOW: - return "FORCE_ALLOW"; - default: - throw new IllegalArgumentException("Unknown effect " + effect); - } - } - - private static boolean isValidEffect(int effect) { - return effect == DENY - || effect == FORCE_ALLOW; - } -} diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java index 5ed282cd8b1f..111b95a05d24 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java @@ -16,7 +16,7 @@ package com.android.server.integrity.parser; -import com.android.server.integrity.model.Rule; +import android.content.integrity.Rule; import java.io.InputStream; import java.util.List; diff --git a/services/core/java/com/android/server/integrity/parser/RuleParser.java b/services/core/java/com/android/server/integrity/parser/RuleParser.java index 08e0a5db8c20..4e1f9141c8ed 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleParser.java @@ -16,7 +16,7 @@ package com.android.server.integrity.parser; -import com.android.server.integrity.model.Rule; +import android.content.integrity.Rule; import java.io.InputStream; import java.util.List; diff --git a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java index 1508c273f553..1212a083bfaa 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java @@ -16,13 +16,12 @@ package com.android.server.integrity.parser; +import android.content.integrity.AtomicFormula; +import android.content.integrity.CompoundFormula; +import android.content.integrity.Formula; +import android.content.integrity.Rule; import android.util.Xml; -import com.android.server.integrity.model.AtomicFormula; -import com.android.server.integrity.model.Formula; -import com.android.server.integrity.model.OpenFormula; -import com.android.server.integrity.model.Rule; - import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -34,9 +33,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -/** - * A helper class to parse rules into the {@link Rule} model from Xml representation. - */ +/** A helper class to parse rules into the {@link Rule} model from Xml representation. */ public final class RuleXmlParser implements RuleParser { public static final String TAG = "RuleXmlParser"; @@ -90,7 +87,8 @@ public final class RuleXmlParser implements RuleParser { // corrupt in the XML, it will be skipped to the next rule. if (!nodeName.equals(RULE_LIST_TAG)) { throw new RuntimeException( - String.format("Rules must start with RuleList tag. Found: %s at %s", + String.format( + "Rules must start with RuleList tag. Found: %s at %s", nodeName, parser.getPositionDescription())); } @@ -141,8 +139,8 @@ public final class RuleXmlParser implements RuleParser { private static Formula parseOpenFormula(XmlPullParser parser) throws IOException, XmlPullParserException { - int connector = Integer.parseInt( - extractAttributeValue(parser, CONNECTOR_ATTRIBUTE).orElse("-1")); + int connector = + Integer.parseInt(extractAttributeValue(parser, CONNECTOR_ATTRIBUTE).orElse("-1")); List formulas = new ArrayList<>(); int eventType; @@ -171,17 +169,17 @@ public final class RuleXmlParser implements RuleParser { } } - return new OpenFormula(connector, formulas); + return new CompoundFormula(connector, formulas); } private static Formula parseAtomicFormula(XmlPullParser parser) throws IOException, XmlPullParserException { int key = Integer.parseInt(extractAttributeValue(parser, KEY_ATTRIBUTE).orElse("-1")); - int operator = Integer.parseInt( - extractAttributeValue(parser, OPERATOR_ATTRIBUTE).orElse("-1")); + int operator = + Integer.parseInt(extractAttributeValue(parser, OPERATOR_ATTRIBUTE).orElse("-1")); String value = extractAttributeValue(parser, VALUE_ATTRIBUTE).orElse(null); - String isHashedValue = extractAttributeValue(parser, IS_HASHED_VALUE_ATTRIBUTE).orElse( - null); + String isHashedValue = + extractAttributeValue(parser, IS_HASHED_VALUE_ATTRIBUTE).orElse(null); int eventType; while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -192,15 +190,18 @@ public final class RuleXmlParser implements RuleParser { return constructAtomicFormulaBasedOnKey(key, operator, value, isHashedValue); } - private static Formula constructAtomicFormulaBasedOnKey(@AtomicFormula.Key int key, - @AtomicFormula.Operator int operator, String value, String isHashedValue) { + private static Formula constructAtomicFormulaBasedOnKey( + @AtomicFormula.Key int key, + @AtomicFormula.Operator int operator, + String value, + String isHashedValue) { switch (key) { case AtomicFormula.PACKAGE_NAME: case AtomicFormula.INSTALLER_NAME: case AtomicFormula.APP_CERTIFICATE: case AtomicFormula.INSTALLER_CERTIFICATE: - return new AtomicFormula.StringAtomicFormula(key, value, - Boolean.parseBoolean(isHashedValue)); + return new AtomicFormula.StringAtomicFormula( + key, value, Boolean.parseBoolean(isHashedValue)); case AtomicFormula.PRE_INSTALLED: return new AtomicFormula.BooleanAtomicFormula(key, Boolean.parseBoolean(value)); case AtomicFormula.VERSION_CODE: diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java index ee95d2bb4d69..ace15bdfb028 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java @@ -16,7 +16,7 @@ package com.android.server.integrity.serializer; -import com.android.server.integrity.model.Rule; +import android.content.integrity.Rule; import java.io.OutputStream; import java.util.List; diff --git a/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java index 5c99c5a4c2da..c45f6beeffe1 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java @@ -16,7 +16,7 @@ package com.android.server.integrity.serializer; -import com.android.server.integrity.model.Rule; +import android.content.integrity.Rule; import java.io.OutputStream; import java.util.List; diff --git a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java index f2ba756ee0d1..5dd7891d15f9 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java @@ -16,13 +16,12 @@ package com.android.server.integrity.serializer; +import android.content.integrity.AtomicFormula; +import android.content.integrity.CompoundFormula; +import android.content.integrity.Formula; +import android.content.integrity.Rule; import android.util.Xml; -import com.android.server.integrity.model.AtomicFormula; -import com.android.server.integrity.model.Formula; -import com.android.server.integrity.model.OpenFormula; -import com.android.server.integrity.model.Rule; - import org.xmlpull.v1.XmlSerializer; import java.io.IOException; @@ -31,9 +30,7 @@ import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.util.List; -/** - * A helper class to serialize rules from the {@link Rule} model to Xml representation. - */ +/** A helper class to serialize rules from the {@link Rule} model to Xml representation. */ public class RuleXmlSerializer implements RuleSerializer { public static final String TAG = "RuleXmlSerializer"; @@ -97,23 +94,23 @@ public class RuleXmlSerializer implements RuleSerializer { private void serializeFormula(Formula formula, XmlSerializer xmlSerializer) throws IOException { if (formula instanceof AtomicFormula) { serializeAtomicFormula((AtomicFormula) formula, xmlSerializer); - } else if (formula instanceof OpenFormula) { - serializeOpenFormula((OpenFormula) formula, xmlSerializer); + } else if (formula instanceof CompoundFormula) { + serializeOpenFormula((CompoundFormula) formula, xmlSerializer); } else { throw new IllegalArgumentException( String.format("Invalid formula type: %s", formula.getClass())); } } - private void serializeOpenFormula(OpenFormula openFormula, XmlSerializer xmlSerializer) + private void serializeOpenFormula(CompoundFormula compoundFormula, XmlSerializer xmlSerializer) throws IOException { - if (openFormula == null) { + if (compoundFormula == null) { return; } xmlSerializer.startTag(NAMESPACE, OPEN_FORMULA_TAG); - serializeAttributeValue(CONNECTOR_ATTRIBUTE, String.valueOf(openFormula.getConnector()), + serializeAttributeValue(CONNECTOR_ATTRIBUTE, String.valueOf(compoundFormula.getConnector()), xmlSerializer); - for (Formula formula : openFormula.getFormulas()) { + for (Formula formula : compoundFormula.getFormulas()) { serializeFormula(formula, xmlSerializer); } xmlSerializer.endTag(NAMESPACE, OPEN_FORMULA_TAG); diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java index d6773d45c5ed..7a070ee72b5d 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java @@ -21,12 +21,13 @@ import static com.android.server.integrity.model.IntegrityCheckResult.Effect.DEN import static org.junit.Assert.assertEquals; -import com.android.server.integrity.model.AppInstallMetadata; -import com.android.server.integrity.model.AtomicFormula; -import com.android.server.integrity.model.AtomicFormula.StringAtomicFormula; +import android.content.integrity.AppInstallMetadata; +import android.content.integrity.AtomicFormula; +import android.content.integrity.AtomicFormula.StringAtomicFormula; +import android.content.integrity.CompoundFormula; +import android.content.integrity.Rule; + import com.android.server.integrity.model.IntegrityCheckResult; -import com.android.server.integrity.model.OpenFormula; -import com.android.server.integrity.model.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -63,7 +64,9 @@ public class RuleEvaluatorTest { public void testEvaluateRules_noMatchedRules_allow() { Rule rule1 = new Rule( - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_2, + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + PACKAGE_NAME_2, /* isHashedValue= */ false), Rule.DENY); @@ -77,12 +80,16 @@ public class RuleEvaluatorTest { public void testEvaluateRules_oneMatch_deny() { Rule rule1 = new Rule( - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_1, + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + PACKAGE_NAME_1, /* isHashedValue= */ false), Rule.DENY); Rule rule2 = new Rule( - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_2, + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + PACKAGE_NAME_2, /* isHashedValue= */ false), Rule.DENY); @@ -97,19 +104,24 @@ public class RuleEvaluatorTest { public void testEvaluateRules_multipleMatches_deny() { Rule rule1 = new Rule( - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_1, + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + PACKAGE_NAME_1, /* isHashedValue= */ false), Rule.DENY); - OpenFormula openFormula2 = - new OpenFormula( - OpenFormula.AND, + CompoundFormula compoundFormula2 = + new CompoundFormula( + CompoundFormula.AND, Arrays.asList( - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_1, + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + PACKAGE_NAME_1, /* isHashedValue= */ false), new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, APP_CERTIFICATE, + AtomicFormula.APP_CERTIFICATE, + APP_CERTIFICATE, /* isHashedValue= */ false))); - Rule rule2 = new Rule(openFormula2, Rule.DENY); + Rule rule2 = new Rule(compoundFormula2, Rule.DENY); IntegrityCheckResult result = RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA); @@ -120,14 +132,15 @@ public class RuleEvaluatorTest { @Test public void testEvaluateRules_ruleWithNot_deny() { - OpenFormula openFormula = - new OpenFormula( - OpenFormula.NOT, + CompoundFormula compoundFormula = + new CompoundFormula( + CompoundFormula.NOT, Collections.singletonList( new StringAtomicFormula( AtomicFormula.PACKAGE_NAME, - PACKAGE_NAME_2, /* isHashedValue= */ false))); - Rule rule = new Rule(openFormula, Rule.DENY); + PACKAGE_NAME_2, + /* isHashedValue= */ false))); + Rule rule = new Rule(compoundFormula, Rule.DENY); IntegrityCheckResult result = RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA); @@ -153,16 +166,19 @@ public class RuleEvaluatorTest { @Test public void testEvaluateRules_validForm_deny() { - OpenFormula openFormula = - new OpenFormula( - OpenFormula.AND, + CompoundFormula compoundFormula = + new CompoundFormula( + CompoundFormula.AND, Arrays.asList( - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_1, + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + PACKAGE_NAME_1, /* isHashedValue= */ false), new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, APP_CERTIFICATE, + AtomicFormula.APP_CERTIFICATE, + APP_CERTIFICATE, /* isHashedValue= */ false))); - Rule rule = new Rule(openFormula, Rule.DENY); + Rule rule = new Rule(compoundFormula, Rule.DENY); IntegrityCheckResult result = RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA); @@ -173,16 +189,19 @@ public class RuleEvaluatorTest { @Test public void testEvaluateRules_ruleNotInDNF_ignoreAndAllow() { - OpenFormula openFormula = - new OpenFormula( - OpenFormula.OR, + CompoundFormula compoundFormula = + new CompoundFormula( + CompoundFormula.OR, Arrays.asList( - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_1, + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + PACKAGE_NAME_1, /* isHashedValue= */ false), new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, APP_CERTIFICATE, + AtomicFormula.APP_CERTIFICATE, + APP_CERTIFICATE, /* isHashedValue= */ false))); - Rule rule = new Rule(openFormula, Rule.DENY); + Rule rule = new Rule(compoundFormula, Rule.DENY); IntegrityCheckResult result = RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA); @@ -191,19 +210,22 @@ public class RuleEvaluatorTest { } @Test - public void testEvaluateRules_openFormulaWithNot_allow() { - OpenFormula openSubFormula = - new OpenFormula( - OpenFormula.AND, + public void testEvaluateRules_compoundFormulaWithNot_allow() { + CompoundFormula openSubFormula = + new CompoundFormula( + CompoundFormula.AND, Arrays.asList( - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_2, + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + PACKAGE_NAME_2, /* isHashedValue= */ false), new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, APP_CERTIFICATE, + AtomicFormula.APP_CERTIFICATE, + APP_CERTIFICATE, /* isHashedValue= */ false))); - OpenFormula openFormula = - new OpenFormula(OpenFormula.NOT, Collections.singletonList(openSubFormula)); - Rule rule = new Rule(openFormula, Rule.DENY); + CompoundFormula compoundFormula = + new CompoundFormula(CompoundFormula.NOT, Collections.singletonList(openSubFormula)); + Rule rule = new Rule(compoundFormula, Rule.DENY); IntegrityCheckResult result = RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA); @@ -215,19 +237,24 @@ public class RuleEvaluatorTest { public void testEvaluateRules_forceAllow() { Rule rule1 = new Rule( - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_1, + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + PACKAGE_NAME_1, /* isHashedValue= */ false), Rule.FORCE_ALLOW); - OpenFormula openFormula2 = - new OpenFormula( - OpenFormula.AND, + CompoundFormula compoundFormula2 = + new CompoundFormula( + CompoundFormula.AND, Arrays.asList( - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_1, + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + PACKAGE_NAME_1, /* isHashedValue= */ false), new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, APP_CERTIFICATE, + AtomicFormula.APP_CERTIFICATE, + APP_CERTIFICATE, /* isHashedValue= */ false))); - Rule rule2 = new Rule(openFormula2, Rule.DENY); + Rule rule2 = new Rule(compoundFormula2, Rule.DENY); IntegrityCheckResult result = RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA); diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/AtomicFormulaTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/AtomicFormulaTest.java deleted file mode 100644 index 988e18b8e194..000000000000 --- a/services/tests/servicestests/src/com/android/server/integrity/model/AtomicFormulaTest.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.model; - -import static com.android.server.testutils.TestUtils.assertExpectException; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.os.Parcel; - -import com.android.server.integrity.model.AtomicFormula.BooleanAtomicFormula; -import com.android.server.integrity.model.AtomicFormula.IntAtomicFormula; -import com.android.server.integrity.model.AtomicFormula.StringAtomicFormula; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -public class AtomicFormulaTest { - - @Test - public void testValidAtomicFormula_stringValue() { - StringAtomicFormula stringAtomicFormula = - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, - "com.test.app", /* isHashedValue= */ false); - - assertEquals(AtomicFormula.PACKAGE_NAME, stringAtomicFormula.getKey()); - } - - @Test - public void testValidAtomicFormula_intValue() { - IntAtomicFormula intAtomicFormula = - new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, 1); - - assertEquals(AtomicFormula.VERSION_CODE, intAtomicFormula.getKey()); - } - - @Test - public void testValidAtomicFormula_boolValue() { - BooleanAtomicFormula atomicFormula = - new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true); - - assertEquals(AtomicFormula.PRE_INSTALLED, atomicFormula.getKey()); - } - - @Test - public void testInvalidAtomicFormula_stringValue() { - assertExpectException( - IllegalArgumentException.class, - /* expectedExceptionMessageRegex */ - String.format( - "Key VERSION_CODE cannot be used with StringAtomicFormula"), - () -> new StringAtomicFormula(AtomicFormula.VERSION_CODE, "test-value", - /* isHashedValue= */ false)); - } - - @Test - public void testInvalidAtomicFormula_intValue() { - assertExpectException( - IllegalArgumentException.class, - /* expectedExceptionMessageRegex */ - String.format( - "Key PACKAGE_NAME cannot be used with IntAtomicFormula"), - () -> new IntAtomicFormula(AtomicFormula.PACKAGE_NAME, AtomicFormula.EQ, 1)); - } - - @Test - public void testInvalidAtomicFormula_boolValue() { - assertExpectException( - IllegalArgumentException.class, - /* expectedExceptionMessageRegex */ - String.format( - "Key PACKAGE_NAME cannot be used with BooleanAtomicFormula"), - () -> new BooleanAtomicFormula(AtomicFormula.PACKAGE_NAME, true)); - } - - @Test - public void testIsSatisfiable_string_true() { - StringAtomicFormula stringAtomicFormula = - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, - "com.test.app", /* isHashedValue= */ false); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setPackageName("com.test.app").build(); - - assertTrue(stringAtomicFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_string_false() { - StringAtomicFormula stringAtomicFormula = - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, - "com.test.app", /* isHashedValue= */ false); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setPackageName("com.foo.bar").build(); - - assertFalse(stringAtomicFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_int_eq_true() { - IntAtomicFormula intAtomicFormula = - new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 0); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setVersionCode(0).build(); - - assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_int_eq_false() { - IntAtomicFormula intAtomicFormula = - new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 0); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setVersionCode(1).build(); - - assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_int_gt_true() { - IntAtomicFormula intAtomicFormula = - new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, 0); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setVersionCode(1).build(); - - assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_int_gt_false() { - IntAtomicFormula intAtomicFormula = - new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, 1); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setVersionCode(0).build(); - - assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_int_ge_true() { - IntAtomicFormula intAtomicFormula = - new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GE, 0); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setVersionCode(1).build(); - - assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_int_ge_false() { - IntAtomicFormula intAtomicFormula = - new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GE, 1); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setVersionCode(0).build(); - - assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_int_lt_true() { - IntAtomicFormula intAtomicFormula = - new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LT, 1); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setVersionCode(0).build(); - - assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_int_lt_false() { - IntAtomicFormula intAtomicFormula = - new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LT, 1); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setVersionCode(2).build(); - - assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_int_le_true() { - IntAtomicFormula intAtomicFormula = - new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, 1); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setVersionCode(0).build(); - - assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_int_le_false() { - IntAtomicFormula intAtomicFormula = - new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, 1); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setVersionCode(2).build(); - - assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_bool_true() { - BooleanAtomicFormula boolFormula = - new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setIsPreInstalled(true).build(); - - assertTrue(boolFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_bool_false() { - BooleanAtomicFormula boolFormula = - new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setIsPreInstalled(false).build(); - - assertFalse(boolFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testParcelUnparcel_string() { - StringAtomicFormula formula = new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "abc", - /* isHashedValue= */ false); - Parcel p = Parcel.obtain(); - formula.writeToParcel(p, 0); - p.setDataPosition(0); - StringAtomicFormula newFormula = StringAtomicFormula.CREATOR.createFromParcel(p); - - assertEquals(formula, newFormula); - } - - @Test - public void testParcelUnparcel_int() { - IntAtomicFormula formula = - new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LT, 1); - Parcel p = Parcel.obtain(); - formula.writeToParcel(p, 0); - p.setDataPosition(0); - IntAtomicFormula newFormula = IntAtomicFormula.CREATOR.createFromParcel(p); - - assertEquals(formula, newFormula); - } - - @Test - public void testParcelUnparcel_bool() { - BooleanAtomicFormula formula = new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true); - Parcel p = Parcel.obtain(); - formula.writeToParcel(p, 0); - p.setDataPosition(0); - BooleanAtomicFormula newFormula = BooleanAtomicFormula.CREATOR.createFromParcel(p); - - assertEquals(formula, newFormula); - } - - @Test - public void testInvalidAtomicFormula_invalidKey() { - assertExpectException( - IllegalArgumentException.class, - /* expectedExceptionMessageRegex */ "Unknown key: -1", - () -> new IntAtomicFormula(/* key= */ -1, AtomicFormula.EQ, 0)); - } - - @Test - public void testInvalidAtomicFormula_invalidOperator() { - assertExpectException( - IllegalArgumentException.class, - /* expectedExceptionMessageRegex */ "Unknown operator: -1", - () -> new IntAtomicFormula(AtomicFormula.VERSION_CODE, /* operator= */ -1, 0)); - } - - /** Returns a builder with all fields filled with some dummy data. */ - private AppInstallMetadata.Builder getAppInstallMetadataBuilder() { - return new AppInstallMetadata.Builder() - .setPackageName("abc") - .setAppCertificate("abc") - .setInstallerCertificate("abc") - .setInstallerName("abc") - .setVersionCode(-1) - .setIsPreInstalled(true); - } -} diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java deleted file mode 100644 index b58ffd7dd6c6..000000000000 --- a/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.model; - -import static com.android.server.testutils.TestUtils.assertExpectException; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.os.Parcel; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.util.Arrays; -import java.util.Collections; - -@RunWith(JUnit4.class) -public class OpenFormulaTest { - - private static final AtomicFormula ATOMIC_FORMULA_1 = - new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "test1", - /* isHashedValue= */ false); - private static final AtomicFormula ATOMIC_FORMULA_2 = - new AtomicFormula.IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1); - - @Test - public void testValidOpenFormula() { - OpenFormula openFormula = - new OpenFormula(OpenFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); - - assertEquals(OpenFormula.AND, openFormula.getConnector()); - assertEquals(Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2), openFormula.getFormulas()); - } - - @Test - public void testValidateAuxiliaryFormula_binaryConnectors() { - assertExpectException( - IllegalArgumentException.class, - /* expectedExceptionMessageRegex */ - "Connector AND must have at least 2 formulas", - () -> - new OpenFormula( - OpenFormula.AND, Collections.singletonList(ATOMIC_FORMULA_1))); - } - - @Test - public void testValidateAuxiliaryFormula_unaryConnectors() { - assertExpectException( - IllegalArgumentException.class, - /* expectedExceptionMessageRegex */ - "Connector NOT must have 1 formula only", - () -> - new OpenFormula( - OpenFormula.NOT, - Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2))); - } - - @Test - public void testIsSatisfiable_notFalse_true() { - OpenFormula openFormula = new OpenFormula(OpenFormula.NOT, - Collections.singletonList(ATOMIC_FORMULA_1)); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setPackageName("test2").build(); - // validate assumptions about the metadata - assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); - - assertTrue(openFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_notTrue_false() { - OpenFormula openFormula = new OpenFormula(OpenFormula.NOT, - Collections.singletonList(ATOMIC_FORMULA_1)); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setPackageName("test1").build(); - // validate assumptions about the metadata - assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); - - assertFalse(openFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_trueAndTrue_true() { - OpenFormula openFormula = - new OpenFormula(OpenFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(1).build(); - // validate assumptions about the metadata - assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); - assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); - - assertTrue(openFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_trueAndFalse_false() { - OpenFormula openFormula = - new OpenFormula(OpenFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(2).build(); - // validate assumptions about the metadata - assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); - assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); - - assertFalse(openFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_falseAndTrue_false() { - OpenFormula openFormula = - new OpenFormula(OpenFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(1).build(); - // validate assumptions about the metadata - assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); - assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); - - assertFalse(openFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_falseAndFalse_false() { - OpenFormula openFormula = - new OpenFormula(OpenFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(2).build(); - // validate assumptions about the metadata - assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); - assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); - - assertFalse(openFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_trueOrTrue_true() { - OpenFormula openFormula = - new OpenFormula(OpenFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(1).build(); - // validate assumptions about the metadata - assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); - assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); - - assertTrue(openFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_trueOrFalse_true() { - OpenFormula openFormula = - new OpenFormula(OpenFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(2).build(); - // validate assumptions about the metadata - assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); - assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); - - assertTrue(openFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_falseOrTrue_true() { - OpenFormula openFormula = - new OpenFormula(OpenFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(1).build(); - // validate assumptions about the metadata - assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); - assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); - - assertTrue(openFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testIsSatisfiable_falseOrFalse_false() { - OpenFormula openFormula = - new OpenFormula(OpenFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); - AppInstallMetadata appInstallMetadata = - getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(2).build(); - // validate assumptions about the metadata - assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata)); - assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata)); - - assertFalse(openFormula.isSatisfied(appInstallMetadata)); - } - - @Test - public void testParcelUnparcel() { - OpenFormula formula = - new OpenFormula(OpenFormula.AND, Arrays.asList(ATOMIC_FORMULA_2, ATOMIC_FORMULA_1)); - Parcel p = Parcel.obtain(); - formula.writeToParcel(p, 0); - p.setDataPosition(0); - OpenFormula newFormula = OpenFormula.CREATOR.createFromParcel(p); - - assertEquals(formula, newFormula); - } - - @Test - public void testInvalidOpenFormula_invalidConnector() { - assertExpectException( - IllegalArgumentException.class, - /* expectedExceptionMessageRegex */ "Unknown connector: -1", - () -> new OpenFormula(/* connector= */ -1, - Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2))); - } - - /** Returns a builder with all fields filled with some dummy data. */ - private AppInstallMetadata.Builder getAppInstallMetadataBuilder() { - return new AppInstallMetadata.Builder() - .setPackageName("abc") - .setAppCertificate("abc") - .setInstallerCertificate("abc") - .setInstallerName("abc") - .setVersionCode(-1) - .setIsPreInstalled(true); - } -} diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java deleted file mode 100644 index cad392826d42..000000000000 --- a/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.model; - -import static com.android.server.testutils.TestUtils.assertExpectException; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; - -import android.os.Parcel; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.util.Arrays; - -@RunWith(JUnit4.class) -public class RuleTest { - - private static final @Rule.Effect int DENY_EFFECT = Rule.DENY; - private static final String PACKAGE_NAME = "com.test.app"; - private static final String APP_CERTIFICATE = "test_cert"; - private static final Formula PACKAGE_NAME_ATOMIC_FORMULA = - new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME, - /* isHashedValue= */ false); - private static final Formula APP_CERTIFICATE_ATOMIC_FORMULA = - new AtomicFormula.StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, APP_CERTIFICATE, - /* isHashedValue= */ false); - - @Test - public void testValidRule() { - Rule validRule = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT); - - assertEquals(PACKAGE_NAME_ATOMIC_FORMULA, validRule.getFormula()); - assertEquals(DENY_EFFECT, validRule.getEffect()); - } - - @Test - public void testInvalidRule_invalidFormula() { - assertExpectException( - NullPointerException.class, - /* expectedExceptionMessageRegex */ null, - () -> new Rule(null, DENY_EFFECT)); - } - - @Test - public void testToString() { - OpenFormula openFormula = - new OpenFormula( - OpenFormula.AND, - Arrays.asList(PACKAGE_NAME_ATOMIC_FORMULA, APP_CERTIFICATE_ATOMIC_FORMULA)); - Rule rule = new Rule(openFormula, Rule.DENY); - - assertEquals( - String.format( - "Rule: (PACKAGE_NAME EQ %s) AND (APP_CERTIFICATE EQ %s), DENY", - PACKAGE_NAME, APP_CERTIFICATE), - rule.toString()); - } - - @Test - public void testEquals_trueCase() { - Rule rule1 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT); - Rule rule2 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT); - - assertEquals(rule1, rule2); - } - - @Test - public void testEquals_falseCase() { - Rule rule1 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT); - Rule rule2 = new Rule(APP_CERTIFICATE_ATOMIC_FORMULA, DENY_EFFECT); - - assertNotEquals(rule1, rule2); - } - - @Test - public void testParcelUnparcel() { - Rule rule = - new Rule( - new OpenFormula( - OpenFormula.AND, - Arrays.asList( - APP_CERTIFICATE_ATOMIC_FORMULA, - new OpenFormula( - OpenFormula.NOT, - Arrays.asList(PACKAGE_NAME_ATOMIC_FORMULA)))), - Rule.DENY); - Parcel p = Parcel.obtain(); - rule.writeToParcel(p, 0); - p.setDataPosition(0); - Rule newRule = Rule.CREATOR.createFromParcel(p); - - assertEquals(newRule, rule); - } - - @Test - public void testInvalidRule_invalidEffect() { - assertExpectException( - IllegalArgumentException.class, - /* expectedExceptionMessageRegex */ "Unknown effect: -1", - () -> new Rule(PACKAGE_NAME_ATOMIC_FORMULA, /* effect= */ -1)); - } -} diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java index 7d71cd4c1b7f..495923d9ccf9 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java @@ -20,9 +20,9 @@ import static com.android.server.testutils.TestUtils.assertExpectException; import static com.google.common.truth.Truth.assertThat; -import com.android.server.integrity.model.AtomicFormula; -import com.android.server.integrity.model.OpenFormula; -import com.android.server.integrity.model.Rule; +import android.content.integrity.AtomicFormula; +import android.content.integrity.CompoundFormula; +import android.content.integrity.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,27 +40,37 @@ import java.util.Map; public class RuleXmlParserTest { @Test - public void testXmlStream_validOpenFormula() throws Exception { + public void testXmlStream_validCompoundFormula() throws Exception { Map atomicFormulaAttrs = new HashMap<>(); atomicFormulaAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); atomicFormulaAttrs.put("V", "com.app.test"); - String ruleXmlOpenFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.NOT)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", atomicFormulaAttrs, /* closed= */ true) - + "" - + "" - + ""; + String ruleXmlCompoundFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", atomicFormulaAttrs, /* closed= */ true) + + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); - InputStream inputStream = new ByteArrayInputStream(ruleXmlOpenFormula.getBytes()); - Rule expectedRule = new Rule(new OpenFormula(OpenFormula.NOT, Collections.singletonList( - new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test", - /* isHashedValue= */ false))), - Rule.DENY); + InputStream inputStream = new ByteArrayInputStream(ruleXmlCompoundFormula.getBytes()); + Rule expectedRule = + new Rule( + new CompoundFormula( + CompoundFormula.NOT, + Collections.singletonList( + new AtomicFormula.StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + "com.app.test", + /* isHashedValue= */ false))), + Rule.DENY); List rules = xmlParser.parse(inputStream); @@ -68,127 +78,172 @@ public class RuleXmlParserTest { } @Test - public void testXmlString_validOpenFormula_notConnector() throws Exception { + public void testXmlString_validCompoundFormula_notConnector() throws Exception { Map packageNameAttrs = new HashMap<>(); packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); packageNameAttrs.put("V", "com.app.test"); - String ruleXmlOpenFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.NOT)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) - + "" - + "" - + ""; + String ruleXmlCompoundFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); - Rule expectedRule = new Rule(new OpenFormula(OpenFormula.NOT, Collections.singletonList( - new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test", - /* isHashedValue= */ false))), - Rule.DENY); - - List rules = xmlParser.parse(ruleXmlOpenFormula); + Rule expectedRule = + new Rule( + new CompoundFormula( + CompoundFormula.NOT, + Collections.singletonList( + new AtomicFormula.StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + "com.app.test", + /* isHashedValue= */ false))), + Rule.DENY); + + List rules = xmlParser.parse(ruleXmlCompoundFormula); assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); } @Test - public void testXmlString_validOpenFormula_andConnector() throws Exception { + public void testXmlString_validCompoundFormula_andConnector() throws Exception { Map packageNameAttrs = new HashMap<>(); packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); packageNameAttrs.put("V", "com.app.test"); Map appCertificateAttrs = new HashMap<>(); appCertificateAttrs.put("K", String.valueOf(AtomicFormula.APP_CERTIFICATE)); appCertificateAttrs.put("V", "test_cert"); - String ruleXmlOpenFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.AND)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) - + generateTagWithAttribute(/* tag= */ "AF", appCertificateAttrs, /* closed= */ true) - + "" - + "" - + ""; + String ruleXmlCompoundFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap("C", String.valueOf(CompoundFormula.AND)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + generateTagWithAttribute( + /* tag= */ "AF", appCertificateAttrs, /* closed= */ true) + + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); - Rule expectedRule = new Rule(new OpenFormula(OpenFormula.AND, Arrays.asList( - new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test", - /* isHashedValue= */ false), - new AtomicFormula.StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, "test_cert", - /* isHashedValue= */ false))), - Rule.DENY); - - List rules = xmlParser.parse(ruleXmlOpenFormula); + Rule expectedRule = + new Rule( + new CompoundFormula( + CompoundFormula.AND, + Arrays.asList( + new AtomicFormula.StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + "com.app.test", + /* isHashedValue= */ false), + new AtomicFormula.StringAtomicFormula( + AtomicFormula.APP_CERTIFICATE, + "test_cert", + /* isHashedValue= */ false))), + Rule.DENY); + List rules = xmlParser.parse(ruleXmlCompoundFormula); assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); } @Test - public void testXmlString_validOpenFormula_orConnector() throws Exception { + public void testXmlString_validCompoundFormula_orConnector() throws Exception { Map packageNameAttrs = new HashMap<>(); packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); packageNameAttrs.put("V", "com.app.test"); Map appCertificateAttrs = new HashMap<>(); appCertificateAttrs.put("K", String.valueOf(AtomicFormula.APP_CERTIFICATE)); appCertificateAttrs.put("V", "test_cert"); - String ruleXmlOpenFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.OR)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) - + generateTagWithAttribute(/* tag= */ "AF", appCertificateAttrs, /* closed= */ true) - + "" - + "" - + ""; + String ruleXmlCompoundFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap("C", String.valueOf(CompoundFormula.OR)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + generateTagWithAttribute( + /* tag= */ "AF", appCertificateAttrs, /* closed= */ true) + + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); - Rule expectedRule = new Rule(new OpenFormula(OpenFormula.OR, Arrays.asList( - new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test", - /* isHashedValue= */ false), - new AtomicFormula.StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, "test_cert", - /* isHashedValue= */ false))), - Rule.DENY); - - List rules = xmlParser.parse(ruleXmlOpenFormula); + Rule expectedRule = + new Rule( + new CompoundFormula( + CompoundFormula.OR, + Arrays.asList( + new AtomicFormula.StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + "com.app.test", + /* isHashedValue= */ false), + new AtomicFormula.StringAtomicFormula( + AtomicFormula.APP_CERTIFICATE, + "test_cert", + /* isHashedValue= */ false))), + Rule.DENY); + + List rules = xmlParser.parse(ruleXmlCompoundFormula); assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); } @Test - public void testXmlString_validOpenFormula_differentTagOrder() throws Exception { + public void testXmlString_validCompoundFormula_differentTagOrder() throws Exception { Map packageNameAttrs = new HashMap<>(); packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); packageNameAttrs.put("V", "com.app.test"); - String ruleXmlOpenFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.NOT)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) - + "" - + "" - + ""; + String ruleXmlCompoundFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); - Rule expectedRule = new Rule(new OpenFormula(OpenFormula.NOT, Collections.singletonList( - new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test", - /* isHashedValue= */ false))), - Rule.DENY); - - List rules = xmlParser.parse(ruleXmlOpenFormula); + Rule expectedRule = + new Rule( + new CompoundFormula( + CompoundFormula.NOT, + Collections.singletonList( + new AtomicFormula.StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + "com.app.test", + /* isHashedValue= */ false))), + Rule.DENY); + + List rules = xmlParser.parse(ruleXmlCompoundFormula); assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); } @Test - public void testXmlString_invalidOpenFormula_invalidNumberOfFormulas() throws Exception { + public void testXmlString_invalidCompoundFormula_invalidNumberOfFormulas() throws Exception { Map packageNameAttrs = new HashMap<>(); packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); packageNameAttrs.put("V", "com.app.test"); @@ -196,98 +251,116 @@ public class RuleXmlParserTest { versionCodeAttrs.put("K", String.valueOf(AtomicFormula.VERSION_CODE)); versionCodeAttrs.put("O", String.valueOf(AtomicFormula.EQ)); versionCodeAttrs.put("V", "1"); - String ruleXmlOpenFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.NOT)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) - + generateTagWithAttribute(/* tag= */ "AF", versionCodeAttrs, /* closed= */ true) - + "" - + "" - + ""; + String ruleXmlCompoundFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + generateTagWithAttribute( + /* tag= */ "AF", versionCodeAttrs, /* closed= */ true) + + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( RuleParseException.class, /* expectedExceptionMessageRegex */ "Connector NOT must have 1 formula only", - () -> xmlParser.parse(ruleXmlOpenFormula)); + () -> xmlParser.parse(ruleXmlCompoundFormula)); } @Test - public void testXmlString_invalidOpenFormula_invalidOperator() throws Exception { + public void testXmlString_invalidCompoundFormula_invalidOperator() throws Exception { Map packageNameAttrs = new HashMap<>(); packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); packageNameAttrs.put("O", "INVALID_OPERATOR"); packageNameAttrs.put("V", "com.app.test"); - String ruleXmlOpenFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.NOT)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) - + "" - + "" - + ""; + String ruleXmlCompoundFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( RuleParseException.class, /* expectedExceptionMessageRegex */ "For input string: \"INVALID_OPERATOR\"", - () -> xmlParser.parse(ruleXmlOpenFormula)); + () -> xmlParser.parse(ruleXmlCompoundFormula)); } @Test - public void testXmlString_invalidOpenFormula_invalidEffect() throws Exception { + public void testXmlString_invalidCompoundFormula_invalidEffect() throws Exception { Map packageNameAttrs = new HashMap<>(); packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); packageNameAttrs.put("V", "com.app.test"); - String ruleXmlOpenFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", "INVALID_EFFECT"), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.NOT)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) - + "" - + "" - + ""; + String ruleXmlCompoundFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", "INVALID_EFFECT"), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( RuleParseException.class, /* expectedExceptionMessageRegex */ "For input string: \"INVALID_EFFECT\"", - () -> xmlParser.parse(ruleXmlOpenFormula)); + () -> xmlParser.parse(ruleXmlCompoundFormula)); } @Test - public void testXmlString_invalidOpenFormula_invalidTags() throws Exception { + public void testXmlString_invalidCompoundFormula_invalidTags() throws Exception { Map packageNameAttrs = new HashMap<>(); packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); packageNameAttrs.put("V", "com.app.test"); - String ruleXmlOpenFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.NOT)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "InvalidAtomicFormula", - packageNameAttrs, /* closed= */ true) - + "" - + "" - + ""; + String ruleXmlCompoundFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "InvalidAtomicFormula", + packageNameAttrs, + /* closed= */ true) + + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( RuleParseException.class, /* expectedExceptionMessageRegex */ "Found unexpected tag: InvalidAtomicFormula", - () -> xmlParser.parse(ruleXmlOpenFormula)); + () -> xmlParser.parse(ruleXmlCompoundFormula)); } @Test @@ -295,18 +368,24 @@ public class RuleXmlParserTest { Map packageNameAttrs = new HashMap<>(); packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); packageNameAttrs.put("V", "com.app.test"); - String ruleXmlAtomicFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) - + "" - + ""; + String ruleXmlAtomicFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); - Rule expectedRule = new Rule( - new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test", - /* isHashedValue= */ false), - Rule.DENY); + Rule expectedRule = + new Rule( + new AtomicFormula.StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + "com.app.test", + /* isHashedValue= */ false), + Rule.DENY); List rules = xmlParser.parse(ruleXmlAtomicFormula); @@ -319,17 +398,22 @@ public class RuleXmlParserTest { versionCodeAttrs.put("K", String.valueOf(AtomicFormula.VERSION_CODE)); versionCodeAttrs.put("O", String.valueOf(AtomicFormula.EQ)); versionCodeAttrs.put("V", "1"); - String ruleXmlAtomicFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", versionCodeAttrs, /* closed= */ true) - + "" - + ""; + String ruleXmlAtomicFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", versionCodeAttrs, /* closed= */ true) + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); - Rule expectedRule = new Rule( - new AtomicFormula.IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1), - Rule.DENY); + Rule expectedRule = + new Rule( + new AtomicFormula.IntAtomicFormula( + AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1), + Rule.DENY); List rules = xmlParser.parse(ruleXmlAtomicFormula); @@ -341,17 +425,21 @@ public class RuleXmlParserTest { Map preInstalledAttrs = new HashMap<>(); preInstalledAttrs.put("K", String.valueOf(AtomicFormula.PRE_INSTALLED)); preInstalledAttrs.put("V", "true"); - String ruleXmlAtomicFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", preInstalledAttrs, /* closed= */ true) - + "" - + ""; + String ruleXmlAtomicFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", preInstalledAttrs, /* closed= */ true) + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); - Rule expectedRule = new Rule( - new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true), - Rule.DENY); + Rule expectedRule = + new Rule( + new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true), + Rule.DENY); List rules = xmlParser.parse(ruleXmlAtomicFormula); @@ -363,18 +451,24 @@ public class RuleXmlParserTest { Map packageNameAttrs = new HashMap<>(); packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); packageNameAttrs.put("V", "com.app.test"); - String ruleXmlAtomicFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) - + "" - + ""; + String ruleXmlAtomicFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); - Rule expectedRule = new Rule( - new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test", - /* isHashedValue= */ false), - Rule.DENY); + Rule expectedRule = + new Rule( + new AtomicFormula.StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + "com.app.test", + /* isHashedValue= */ false), + Rule.DENY); List rules = xmlParser.parse(ruleXmlAtomicFormula); @@ -386,13 +480,16 @@ public class RuleXmlParserTest { Map packageNameAttrs = new HashMap<>(); packageNameAttrs.put("BadKey", String.valueOf(AtomicFormula.PACKAGE_NAME)); packageNameAttrs.put("V", "com.app.test"); - String ruleXmlAtomicFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) - + "" - + ""; + String ruleXmlAtomicFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( @@ -406,13 +503,16 @@ public class RuleXmlParserTest { Map packageNameAttrs = new HashMap<>(); packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); packageNameAttrs.put("V", "com.app.test"); - String ruleXmlAtomicFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("BadEffect", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) - + "" - + ""; + String ruleXmlAtomicFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("BadEffect", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( RuleParseException.class, @@ -421,26 +521,31 @@ public class RuleXmlParserTest { } @Test - public void testXmlString_invalidOpenFormula_invalidAttribute() throws Exception { + public void testXmlString_invalidCompoundFormula_invalidAttribute() throws Exception { Map packageNameAttrs = new HashMap<>(); packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); packageNameAttrs.put("V", "com.app.test"); - String ruleXmlOpenFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("BadConnector", String.valueOf(OpenFormula.NOT)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) - + "" - + "" - + ""; + String ruleXmlCompoundFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap( + "BadConnector", String.valueOf(CompoundFormula.NOT)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( RuleParseException.class, /* expectedExceptionMessageRegex */ "Unknown connector: -1", - () -> xmlParser.parse(ruleXmlOpenFormula)); + () -> xmlParser.parse(ruleXmlCompoundFormula)); } @Test @@ -449,13 +554,16 @@ public class RuleXmlParserTest { packageNameAttrs.put("K", String.valueOf(AtomicFormula.VERSION_CODE)); packageNameAttrs.put("O", String.valueOf(AtomicFormula.EQ)); packageNameAttrs.put("V", "com.app.test"); - String ruleXmlAtomicFormula = "" - + generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) - + "" - + ""; + String ruleXmlAtomicFormula = + "" + + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( @@ -469,15 +577,19 @@ public class RuleXmlParserTest { Map atomicFormulaAttrs = new HashMap<>(); atomicFormulaAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); atomicFormulaAttrs.put("V", "com.app.test"); - String ruleXmlWithNoRuleList = generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.NOT)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", atomicFormulaAttrs, /* closed= */ true) - + "" - + ""; + String ruleXmlWithNoRuleList = + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", atomicFormulaAttrs, /* closed= */ true) + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( @@ -491,15 +603,19 @@ public class RuleXmlParserTest { Map atomicFormulaAttrs = new HashMap<>(); atomicFormulaAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); atomicFormulaAttrs.put("V", "com.app.test"); - String ruleXmlWithNoRuleList = generateTagWithAttribute(/* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.NOT)), - /* closed= */ false) - + generateTagWithAttribute(/* tag= */ "AF", atomicFormulaAttrs, /* closed= */ true) - + "" - + ""; + String ruleXmlWithNoRuleList = + generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", atomicFormulaAttrs, /* closed= */ true) + + "" + + ""; InputStream inputStream = new ByteArrayInputStream(ruleXmlWithNoRuleList.getBytes()); RuleParser xmlParser = new RuleXmlParser(); @@ -509,8 +625,8 @@ public class RuleXmlParserTest { () -> xmlParser.parse(inputStream)); } - private String generateTagWithAttribute(String tag, Map attributeValues, - boolean closed) { + private String generateTagWithAttribute( + String tag, Map attributeValues, boolean closed) { StringBuilder res = new StringBuilder("<"); res.append(tag); for (String attribute : attributeValues.keySet()) { diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java index 7eac0b912bf8..40e89ba1e9c7 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java @@ -20,13 +20,13 @@ import static com.android.server.testutils.TestUtils.assertExpectException; import static org.junit.Assert.assertEquals; -import androidx.annotation.NonNull; +import android.content.integrity.AppInstallMetadata; +import android.content.integrity.AtomicFormula; +import android.content.integrity.CompoundFormula; +import android.content.integrity.Formula; +import android.content.integrity.Rule; -import com.android.server.integrity.model.AppInstallMetadata; -import com.android.server.integrity.model.AtomicFormula; -import com.android.server.integrity.model.Formula; -import com.android.server.integrity.model.OpenFormula; -import com.android.server.integrity.model.Rule; +import androidx.annotation.NonNull; import org.junit.Test; import org.junit.runner.RunWith; @@ -80,7 +80,7 @@ public class RuleXmlSerializerTest { @Test public void testXmlStream_serializeValidOpenFormula() throws Exception { - Rule rule = new Rule(new OpenFormula(OpenFormula.NOT, + Rule rule = new Rule(new CompoundFormula(CompoundFormula.NOT, Collections.singletonList( new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test", /* isHashedValue= */ false))), Rule.DENY); @@ -95,7 +95,7 @@ public class RuleXmlSerializerTest { Collections.singletonMap("E", String.valueOf(Rule.DENY)), /* closed= */ false) + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.NOT)), + Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)), /* closed= */ false) + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) + "" @@ -110,7 +110,7 @@ public class RuleXmlSerializerTest { @Test public void testXmlString_serializeValidOpenFormula_notConnector() throws Exception { - Rule rule = new Rule(new OpenFormula(OpenFormula.NOT, + Rule rule = new Rule(new CompoundFormula(CompoundFormula.NOT, Collections.singletonList( new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test", /* isHashedValue= */ false))), Rule.DENY); @@ -124,7 +124,7 @@ public class RuleXmlSerializerTest { Collections.singletonMap("E", String.valueOf(Rule.DENY)), /* closed= */ false) + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.NOT)), + Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)), /* closed= */ false) + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) + "" @@ -138,7 +138,7 @@ public class RuleXmlSerializerTest { @Test public void testXmlString_serializeValidOpenFormula_andConnector() throws Exception { - Rule rule = new Rule(new OpenFormula(OpenFormula.AND, + Rule rule = new Rule(new CompoundFormula(CompoundFormula.AND, Arrays.asList(new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test", /* isHashedValue= */ false), new AtomicFormula.StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, @@ -157,7 +157,7 @@ public class RuleXmlSerializerTest { Collections.singletonMap("E", String.valueOf(Rule.DENY)), /* closed= */ false) + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.AND)), + Collections.singletonMap("C", String.valueOf(CompoundFormula.AND)), /* closed= */ false) + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) + generateTagWithAttribute(/* tag= */ "AF", appCertificateAttrs, /* closed= */ true) @@ -172,7 +172,7 @@ public class RuleXmlSerializerTest { @Test public void testXmlString_serializeValidOpenFormula_orConnector() throws Exception { - Rule rule = new Rule(new OpenFormula(OpenFormula.OR, + Rule rule = new Rule(new CompoundFormula(CompoundFormula.OR, Arrays.asList(new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test", /* isHashedValue= */ false), new AtomicFormula.StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, @@ -191,7 +191,7 @@ public class RuleXmlSerializerTest { Collections.singletonMap("E", String.valueOf(Rule.DENY)), /* closed= */ false) + generateTagWithAttribute(/* tag= */ "OF", - Collections.singletonMap("C", String.valueOf(OpenFormula.OR)), + Collections.singletonMap("C", String.valueOf(CompoundFormula.OR)), /* closed= */ false) + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true) + generateTagWithAttribute(/* tag= */ "AF", appCertificateAttrs, /* closed= */ true) -- cgit v1.2.3-59-g8ed1b