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