Add rule validations

Validations are added to ensure that created rules are following the
structure defined.

Bug: 141979167
Test: atest RuleTest
Change-Id: I5b54c1a61703e52abeedc3349192a1512f209a57
diff --git a/services/core/java/com/android/server/integrity/TEST_MAPPING b/services/core/java/com/android/server/integrity/TEST_MAPPING
new file mode 100644
index 0000000..b45b4ea
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.integrity."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/integrity/model/Rule.java b/services/core/java/com/android/server/integrity/model/Rule.java
index c227aea..a6e08d8 100644
--- a/services/core/java/com/android/server/integrity/model/Rule.java
+++ b/services/core/java/com/android/server/integrity/model/Rule.java
@@ -16,13 +16,20 @@
 
 package com.android.server.integrity.model;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+
 import android.annotation.Nullable;
 
 /**
  * Represent rules to be used in the rule evaluation engine to match against app installs.
+ *
+ * <p>Instances of this class are immutable.
  */
 public final class Rule {
 
+    // Holds an empty rule instance.
+    public static final Rule EMPTY = new Rule();
+
     enum Key {
         PACKAGE_NAME,
         APP_CERTIFICATE,
@@ -50,25 +57,49 @@
         NOT
     }
 
-    final Formula mFormula;
-    final Effect mEffect;
+    private final Formula mFormula;
+    private final Effect mEffect;
+
+    private Rule() {
+        this.mFormula = null;
+        this.mEffect = null;
+    }
 
     public Rule(Formula formula, Effect effect) {
-        this.mFormula = formula;
-        this.mEffect = effect;
+        this.mFormula = checkNotNull(formula);
+        this.mEffect = checkNotNull(effect);
     }
 
     /**
+     * Indicates whether the rule is empty or not.
+     *
+     * @return {@code true} if the rule is empty, and {@code false} otherwise.
+     */
+    public boolean isEmpty() {
+        return mFormula == null && mEffect == null;
+    }
+
+    public Formula getFormula() {
+        return mFormula;
+    }
+
+    public Effect getEffect() {
+        return mEffect;
+    }
+
+    // TODO: Consider moving the sub-components to their respective model class.
+
+    /**
      * Represents a rule logic/content.
      */
-    abstract class Formula {
+    abstract static class Formula {
 
     }
 
     /**
      * Represents a simple formula consisting of an app install metadata field and a value.
      */
-    public final class AtomicFormula extends Formula {
+    public static final class AtomicFormula extends Formula {
 
         final Key mKey;
         final Operator mOperator;
@@ -113,15 +144,16 @@
     /**
      * Represents a complex formula consisting of other simple and complex formulas.
      */
-    public final class OpenFormula extends Formula {
+    public static final class OpenFormula extends Formula {
 
         final Connector mConnector;
         final Formula mMainFormula;
         final Formula mAuxiliaryFormula;
 
-        public OpenFormula(Connector connector, Formula mainFormula, Formula auxiliaryFormula) {
-            this.mConnector = connector;
-            this.mMainFormula = mainFormula;
+        public OpenFormula(Connector connector, Formula mainFormula,
+                @Nullable Formula auxiliaryFormula) {
+            this.mConnector = checkNotNull(connector);
+            this.mMainFormula = checkNotNull(mainFormula);
             this.mAuxiliaryFormula = auxiliaryFormula;
         }
     }
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
new file mode 100644
index 0000000..4321c21
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.assertNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class RuleTest {
+
+    private static final Rule.Effect DENY_EFFECT = Rule.Effect.DENY;
+    private static final Rule.Formula SIMPLE_FORMULA =
+            new Rule.AtomicFormula(Rule.Key.PACKAGE_NAME, Rule.Operator.EQ, "com.test.app");
+
+    @Test
+    public void testEmptyRule() {
+        Rule emptyRule = Rule.EMPTY;
+
+        assertNull(emptyRule.getFormula());
+        assertNull(emptyRule.getEffect());
+    }
+
+    @Test
+    public void testValidRule() {
+        Rule validRule = new Rule(SIMPLE_FORMULA, DENY_EFFECT);
+
+        assertEquals(SIMPLE_FORMULA, validRule.getFormula());
+        assertEquals(DENY_EFFECT, validRule.getEffect());
+    }
+
+    @Test
+    public void testInvalidRule_invalidEffect() {
+        assertExpectException(
+                NullPointerException.class,
+                /* expectedExceptionMessageRegex */ null,
+                () -> new Rule(SIMPLE_FORMULA, null));
+    }
+
+    @Test
+    public void testInvalidRule_invalidFormula() {
+        assertExpectException(
+                NullPointerException.class,
+                /* expectedExceptionMessageRegex */ null,
+                () -> new Rule(null, DENY_EFFECT));
+    }
+}