• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.content.integrity;
18 
19 import static com.android.internal.util.Preconditions.checkArgument;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.Objects;
35 
36 /**
37  * Represents a compound formula formed by joining other simple and complex formulas with boolean
38  * connectors.
39  *
40  * <p>Instances of this class are immutable.
41  *
42  * @hide
43  */
44 @VisibleForTesting
45 public final class CompoundFormula extends IntegrityFormula implements Parcelable {
46 
47     /** @hide */
48     @IntDef(value = {AND, OR, NOT})
49     @Retention(RetentionPolicy.SOURCE)
50     public @interface Connector {}
51 
52     /** Boolean AND operator. */
53     public static final int AND = 0;
54 
55     /** Boolean OR operator. */
56     public static final int OR = 1;
57 
58     /** Boolean NOT operator. */
59     public static final int NOT = 2;
60 
61     private final @Connector int mConnector;
62     private final @NonNull List<IntegrityFormula> mFormulas;
63 
64     @NonNull
65     public static final Creator<CompoundFormula> CREATOR =
66             new Creator<CompoundFormula>() {
67                 @Override
68                 public CompoundFormula createFromParcel(Parcel in) {
69                     return new CompoundFormula(in);
70                 }
71 
72                 @Override
73                 public CompoundFormula[] newArray(int size) {
74                     return new CompoundFormula[size];
75                 }
76             };
77 
78     /**
79      * Create a new formula from operator and operands.
80      *
81      * @throws IllegalArgumentException if the number of operands is not matching the requirements
82      *                                  for that operator (at least 2 for {@link #AND} and {@link
83      *                                  #OR}, 1 for {@link #NOT}).
84      */
CompoundFormula(@onnector int connector, List<IntegrityFormula> formulas)85     public CompoundFormula(@Connector int connector, List<IntegrityFormula> formulas) {
86         checkArgument(
87                 isValidConnector(connector), "Unknown connector: %d", connector);
88         validateFormulas(connector, formulas);
89         this.mConnector = connector;
90         this.mFormulas = Collections.unmodifiableList(formulas);
91     }
92 
CompoundFormula(Parcel in)93     CompoundFormula(Parcel in) {
94         mConnector = in.readInt();
95         int length = in.readInt();
96         checkArgument(length >= 0, "Must have non-negative length. Got %d", length);
97         mFormulas = new ArrayList<>(length);
98         for (int i = 0; i < length; i++) {
99             mFormulas.add(IntegrityFormula.readFromParcel(in));
100         }
101         validateFormulas(mConnector, mFormulas);
102     }
103 
getConnector()104     public @Connector int getConnector() {
105         return mConnector;
106     }
107 
108     @NonNull
getFormulas()109     public List<IntegrityFormula> getFormulas() {
110         return mFormulas;
111     }
112 
113     @Override
getTag()114     public int getTag() {
115         return IntegrityFormula.COMPOUND_FORMULA_TAG;
116     }
117 
118     @Override
matches(AppInstallMetadata appInstallMetadata)119     public boolean matches(AppInstallMetadata appInstallMetadata) {
120         switch (getConnector()) {
121             case NOT:
122                 return !getFormulas().get(0).matches(appInstallMetadata);
123             case AND:
124                 return getFormulas().stream()
125                         .allMatch(formula -> formula.matches(appInstallMetadata));
126             case OR:
127                 return getFormulas().stream()
128                         .anyMatch(formula -> formula.matches(appInstallMetadata));
129             default:
130                 throw new IllegalArgumentException("Unknown connector " + getConnector());
131         }
132     }
133 
134     @Override
isAppCertificateFormula()135     public boolean isAppCertificateFormula() {
136         return getFormulas().stream().anyMatch(formula -> formula.isAppCertificateFormula());
137     }
138 
139     @Override
isInstallerFormula()140     public boolean isInstallerFormula() {
141         return getFormulas().stream().anyMatch(formula -> formula.isInstallerFormula());
142     }
143 
144     @Override
toString()145     public String toString() {
146         StringBuilder sb = new StringBuilder();
147         if (mFormulas.size() == 1) {
148             sb.append(String.format("%s ", connectorToString(mConnector)));
149             sb.append(mFormulas.get(0).toString());
150         } else {
151             for (int i = 0; i < mFormulas.size(); i++) {
152                 if (i > 0) {
153                     sb.append(String.format(" %s ", connectorToString(mConnector)));
154                 }
155                 sb.append(mFormulas.get(i).toString());
156             }
157         }
158         return sb.toString();
159     }
160 
161     @Override
equals(@ullable Object o)162     public boolean equals(@Nullable Object o) {
163         if (this == o) {
164             return true;
165         }
166         if (o == null || getClass() != o.getClass()) {
167             return false;
168         }
169         CompoundFormula that = (CompoundFormula) o;
170         return mConnector == that.mConnector && mFormulas.equals(that.mFormulas);
171     }
172 
173     @Override
hashCode()174     public int hashCode() {
175         return Objects.hash(mConnector, mFormulas);
176     }
177 
178     @Override
describeContents()179     public int describeContents() {
180         return 0;
181     }
182 
183     @Override
writeToParcel(@onNull Parcel dest, int flags)184     public void writeToParcel(@NonNull Parcel dest, int flags) {
185         dest.writeInt(mConnector);
186         dest.writeInt(mFormulas.size());
187         for (IntegrityFormula formula : mFormulas) {
188             IntegrityFormula.writeToParcel(formula, dest, flags);
189         }
190     }
191 
validateFormulas( @onnector int connector, List<IntegrityFormula> formulas)192     private static void validateFormulas(
193             @Connector int connector, List<IntegrityFormula> formulas) {
194         switch (connector) {
195             case AND:
196             case OR:
197                 checkArgument(
198                         formulas.size() >= 2,
199                         "Connector %s must have at least 2 formulas",
200                         connectorToString(connector));
201                 break;
202             case NOT:
203                 checkArgument(
204                         formulas.size() == 1,
205                         "Connector %s must have 1 formula only",
206                         connectorToString(connector));
207                 break;
208         }
209     }
210 
connectorToString(int connector)211     private static String connectorToString(int connector) {
212         switch (connector) {
213             case AND:
214                 return "AND";
215             case OR:
216                 return "OR";
217             case NOT:
218                 return "NOT";
219             default:
220                 throw new IllegalArgumentException("Unknown connector " + connector);
221         }
222     }
223 
isValidConnector(int connector)224     private static boolean isValidConnector(int connector) {
225         return connector == AND || connector == OR || connector == NOT;
226     }
227 }
228