blob: 76007e67798dd7640389d4fce10547dd651833cd [file] [log] [blame]
Khaled Abdelmohsene959bae2019-10-04 17:01:13 +01001/*
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +00002 * Copyright (C) 2020 The Android Open Source Project
Khaled Abdelmohsene959bae2019-10-04 17:01:13 +01003 *
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
Song Pan75147d52019-11-19 00:57:46 +000017package android.content.integrity;
Khaled Abdelmohsene959bae2019-10-04 17:01:13 +010018
Khaled Abdelmohsenbe0454d2019-10-07 10:24:18 +010019import static com.android.internal.util.Preconditions.checkArgument;
Khaled Abdelmohsenbe0454d2019-10-07 10:24:18 +010020
Song Pan097f65d2019-11-10 18:02:52 +000021import android.annotation.IntDef;
22import android.annotation.NonNull;
Song Pan097f65d2019-11-10 18:02:52 +000023import android.os.Parcel;
24import android.os.Parcelable;
Khaled Abdelmohsene959bae2019-10-04 17:01:13 +010025
Song Pan097f65d2019-11-10 18:02:52 +000026import com.android.internal.annotations.VisibleForTesting;
27
28import java.lang.annotation.Retention;
29import java.lang.annotation.RetentionPolicy;
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +000030import java.nio.charset.StandardCharsets;
31import java.security.MessageDigest;
32import java.security.NoSuchAlgorithmException;
Khaled Abdelmohsenf5f98142019-10-10 17:29:53 +010033import java.util.Objects;
34
Khaled Abdelmohsene959bae2019-10-04 17:01:13 +010035/**
36 * Represents a simple formula consisting of an app install metadata field and a value.
37 *
38 * <p>Instances of this class are immutable.
Song Pan097f65d2019-11-10 18:02:52 +000039 *
40 * @hide
Khaled Abdelmohsene959bae2019-10-04 17:01:13 +010041 */
Song Pan097f65d2019-11-10 18:02:52 +000042@VisibleForTesting
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +000043public abstract class AtomicFormula extends IntegrityFormula {
Khaled Abdelmohsen29be9642019-10-08 18:34:45 +010044
Anton Hansson57b6af02019-12-10 15:38:59 +000045 /** @hide */
Song Pan097f65d2019-11-10 18:02:52 +000046 @IntDef(
47 value = {
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +000048 PACKAGE_NAME,
49 APP_CERTIFICATE,
50 INSTALLER_NAME,
51 INSTALLER_CERTIFICATE,
52 VERSION_CODE,
53 PRE_INSTALLED,
Song Pan097f65d2019-11-10 18:02:52 +000054 })
55 @Retention(RetentionPolicy.SOURCE)
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +000056 public @interface Key {
57 }
Song Pan097f65d2019-11-10 18:02:52 +000058
Anton Hansson57b6af02019-12-10 15:38:59 +000059 /** @hide */
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +000060 @IntDef(value = {EQ, GT, GTE})
Song Pan097f65d2019-11-10 18:02:52 +000061 @Retention(RetentionPolicy.SOURCE)
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +000062 public @interface Operator {
63 }
Song Pan097f65d2019-11-10 18:02:52 +000064
Song Pan44e5aa52019-11-26 11:38:17 +000065 /**
66 * Package name of the app.
67 *
68 * <p>Can only be used in {@link StringAtomicFormula}.
69 */
Song Pan097f65d2019-11-10 18:02:52 +000070 public static final int PACKAGE_NAME = 0;
Song Pan44e5aa52019-11-26 11:38:17 +000071
72 /**
73 * SHA-256 of the app certificate of the app.
74 *
75 * <p>Can only be used in {@link StringAtomicFormula}.
76 */
Song Pan097f65d2019-11-10 18:02:52 +000077 public static final int APP_CERTIFICATE = 1;
Song Pan44e5aa52019-11-26 11:38:17 +000078
79 /**
80 * Package name of the installer. Will be empty string if installed by the system (e.g., adb).
81 *
82 * <p>Can only be used in {@link StringAtomicFormula}.
83 */
Song Pan097f65d2019-11-10 18:02:52 +000084 public static final int INSTALLER_NAME = 2;
Song Pan44e5aa52019-11-26 11:38:17 +000085
86 /**
87 * SHA-256 of the cert of the installer. Will be empty string if installed by the system (e.g.,
88 * adb).
89 *
90 * <p>Can only be used in {@link StringAtomicFormula}.
91 */
Song Pan097f65d2019-11-10 18:02:52 +000092 public static final int INSTALLER_CERTIFICATE = 3;
Song Pan44e5aa52019-11-26 11:38:17 +000093
94 /**
95 * Version code of the app.
96 *
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +000097 * <p>Can only be used in {@link LongAtomicFormula}.
Song Pan44e5aa52019-11-26 11:38:17 +000098 */
Song Pan097f65d2019-11-10 18:02:52 +000099 public static final int VERSION_CODE = 4;
Song Pan44e5aa52019-11-26 11:38:17 +0000100
101 /**
102 * If the app is pre-installed on the device.
103 *
104 * <p>Can only be used in {@link BooleanAtomicFormula}.
105 */
Song Pan097f65d2019-11-10 18:02:52 +0000106 public static final int PRE_INSTALLED = 5;
107
108 public static final int EQ = 0;
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000109 public static final int GT = 1;
110 public static final int GTE = 2;
Song Pan097f65d2019-11-10 18:02:52 +0000111
112 private final @Key int mKey;
113
114 public AtomicFormula(@Key int key) {
Khaled Abdelmohsen5809a5f2019-11-25 18:20:46 +0000115 checkArgument(isValidKey(key), String.format("Unknown key: %d", key));
Song Pan097f65d2019-11-10 18:02:52 +0000116 mKey = key;
Khaled Abdelmohsene959bae2019-10-04 17:01:13 +0100117 }
118
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000119 /** An {@link AtomicFormula} with an key and long value. */
120 public static final class LongAtomicFormula extends AtomicFormula implements Parcelable {
121 private final Long mValue;
122 private final @Operator Integer mOperator;
Song Pan097f65d2019-11-10 18:02:52 +0000123
124 /**
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000125 * Constructs an empty {@link LongAtomicFormula}. This should only be used as a base.
126 *
127 * <p>This formula will always return false.
128 *
129 * @throws IllegalArgumentException if {@code key} cannot be used with long value
130 */
131 public LongAtomicFormula(@Key int key) {
132 super(key);
133 checkArgument(
134 key == VERSION_CODE,
135 String.format(
136 "Key %s cannot be used with LongAtomicFormula", keyToString(key)));
137 mValue = null;
138 mOperator = null;
139 }
140
141 /**
142 * Constructs a new {@link LongAtomicFormula}.
Song Pan097f65d2019-11-10 18:02:52 +0000143 *
144 * <p>This formula will hold if and only if the corresponding information of an install
145 * specified by {@code key} is of the correct relationship to {@code value} as specified by
146 * {@code operator}.
147 *
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000148 * @throws IllegalArgumentException if {@code key} cannot be used with long value
Song Pan097f65d2019-11-10 18:02:52 +0000149 */
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000150 public LongAtomicFormula(@Key int key, @Operator int operator, long value) {
Song Pan097f65d2019-11-10 18:02:52 +0000151 super(key);
152 checkArgument(
153 key == VERSION_CODE,
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000154 String.format(
155 "Key %s cannot be used with LongAtomicFormula", keyToString(key)));
156 checkArgument(
157 isValidOperator(operator), String.format("Unknown operator: %d", operator));
Song Pan097f65d2019-11-10 18:02:52 +0000158 mOperator = operator;
159 mValue = value;
160 }
161
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000162 LongAtomicFormula(Parcel in) {
Song Pan097f65d2019-11-10 18:02:52 +0000163 super(in.readInt());
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000164 mValue = in.readLong();
Song Pan097f65d2019-11-10 18:02:52 +0000165 mOperator = in.readInt();
166 }
167
168 @NonNull
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000169 public static final Creator<LongAtomicFormula> CREATOR =
170 new Creator<LongAtomicFormula>() {
Song Pan097f65d2019-11-10 18:02:52 +0000171 @Override
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000172 public LongAtomicFormula createFromParcel(Parcel in) {
173 return new LongAtomicFormula(in);
Song Pan097f65d2019-11-10 18:02:52 +0000174 }
175
176 @Override
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000177 public LongAtomicFormula[] newArray(int size) {
178 return new LongAtomicFormula[size];
Song Pan097f65d2019-11-10 18:02:52 +0000179 }
180 };
181
182 @Override
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000183 public int getTag() {
184 return IntegrityFormula.LONG_ATOMIC_FORMULA_TAG;
185 }
186
187 @Override
188 public boolean matches(AppInstallMetadata appInstallMetadata) {
189 if (mValue == null || mOperator == null) {
190 return false;
191 }
192
193 long metadataValue = getLongMetadataValue(appInstallMetadata, getKey());
Song Pan097f65d2019-11-10 18:02:52 +0000194 switch (mOperator) {
195 case EQ:
196 return metadataValue == mValue;
Song Pan097f65d2019-11-10 18:02:52 +0000197 case GT:
198 return metadataValue > mValue;
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000199 case GTE:
200 return metadataValue >= mValue;
Song Pan097f65d2019-11-10 18:02:52 +0000201 default:
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000202 throw new IllegalArgumentException(
203 String.format("Unexpected operator %d", mOperator));
Song Pan097f65d2019-11-10 18:02:52 +0000204 }
205 }
206
207 @Override
Omer Nebil Yaveroglu84f7c3f2020-01-29 12:18:10 +0000208 public boolean isAppCertificateFormula() {
209 return false;
210 }
211
212 @Override
213 public boolean isInstallerFormula() {
214 return false;
215 }
216
217 @Override
Song Pan097f65d2019-11-10 18:02:52 +0000218 public String toString() {
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000219 if (mValue == null || mOperator == null) {
220 return String.format("(%s)", keyToString(getKey()));
221 }
Song Pan097f65d2019-11-10 18:02:52 +0000222 return String.format(
223 "(%s %s %s)", keyToString(getKey()), operatorToString(mOperator), mValue);
224 }
225
226 @Override
227 public boolean equals(Object o) {
228 if (this == o) {
229 return true;
230 }
231 if (o == null || getClass() != o.getClass()) {
232 return false;
233 }
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000234 LongAtomicFormula that = (LongAtomicFormula) o;
Song Pan44e5aa52019-11-26 11:38:17 +0000235 return getKey() == that.getKey()
236 && mValue == that.mValue
237 && mOperator == that.mOperator;
Song Pan097f65d2019-11-10 18:02:52 +0000238 }
239
240 @Override
241 public int hashCode() {
242 return Objects.hash(getKey(), mOperator, mValue);
243 }
244
245 @Override
246 public int describeContents() {
247 return 0;
248 }
249
250 @Override
251 public void writeToParcel(@NonNull Parcel dest, int flags) {
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000252 if (mValue == null || mOperator == null) {
253 throw new IllegalStateException("Cannot write an empty LongAtomicFormula.");
254 }
Song Pan097f65d2019-11-10 18:02:52 +0000255 dest.writeInt(getKey());
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000256 dest.writeLong(mValue);
Song Pan097f65d2019-11-10 18:02:52 +0000257 dest.writeInt(mOperator);
258 }
259
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000260 public Long getValue() {
Khaled Abdelmohsen0a181cd2019-11-12 12:24:38 +0000261 return mValue;
262 }
263
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000264 public Integer getOperator() {
Khaled Abdelmohsen0a181cd2019-11-12 12:24:38 +0000265 return mOperator;
266 }
267
Khaled Abdelmohsen5809a5f2019-11-25 18:20:46 +0000268 private static boolean isValidOperator(int operator) {
269 return operator == EQ
Khaled Abdelmohsen5809a5f2019-11-25 18:20:46 +0000270 || operator == GT
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000271 || operator == GTE;
272 }
273
274 private static long getLongMetadataValue(AppInstallMetadata appInstallMetadata, int key) {
275 switch (key) {
276 case AtomicFormula.VERSION_CODE:
277 return appInstallMetadata.getVersionCode();
278 default:
279 throw new IllegalStateException("Unexpected key in IntAtomicFormula" + key);
280 }
Khaled Abdelmohsen5809a5f2019-11-25 18:20:46 +0000281 }
Khaled Abdelmohsene959bae2019-10-04 17:01:13 +0100282 }
283
Song Pan097f65d2019-11-10 18:02:52 +0000284 /** An {@link AtomicFormula} with a key and string value. */
285 public static final class StringAtomicFormula extends AtomicFormula implements Parcelable {
286 private final String mValue;
Khaled Abdelmohsen1efff872019-11-25 16:44:20 +0000287 // Indicates whether the value is the actual value or the hashed value.
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000288 private final Boolean mIsHashedValue;
289
290 /**
291 * Constructs an empty {@link StringAtomicFormula}. This should only be used as a base.
292 *
293 * <p>An empty formula will always match to false.
294 *
295 * @throws IllegalArgumentException if {@code key} cannot be used with string value
296 */
297 public StringAtomicFormula(@Key int key) {
298 super(key);
299 checkArgument(
300 key == PACKAGE_NAME
301 || key == APP_CERTIFICATE
302 || key == INSTALLER_CERTIFICATE
303 || key == INSTALLER_NAME,
304 String.format(
305 "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
306 mValue = null;
307 mIsHashedValue = null;
308 }
Khaled Abdelmohsene959bae2019-10-04 17:01:13 +0100309
Song Pan097f65d2019-11-10 18:02:52 +0000310 /**
311 * Constructs a new {@link StringAtomicFormula}.
312 *
313 * <p>This formula will hold if and only if the corresponding information of an install
314 * specified by {@code key} equals {@code value}.
315 *
Song Pan44e5aa52019-11-26 11:38:17 +0000316 * @throws IllegalArgumentException if {@code key} cannot be used with string value
Song Pan097f65d2019-11-10 18:02:52 +0000317 */
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000318 public StringAtomicFormula(@Key int key, @NonNull String value, boolean isHashed) {
Song Pan097f65d2019-11-10 18:02:52 +0000319 super(key);
320 checkArgument(
321 key == PACKAGE_NAME
322 || key == APP_CERTIFICATE
323 || key == INSTALLER_CERTIFICATE
324 || key == INSTALLER_NAME,
325 String.format(
326 "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
327 mValue = value;
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000328 mIsHashedValue = isHashed;
329 }
330
331 /**
332 * Constructs a new {@link StringAtomicFormula} together with handling the necessary
333 * hashing for the given key.
334 *
335 * <p> The value will be hashed with SHA256 and the hex digest will be computed; for
336 * all cases except when the key is PACKAGE_NAME or INSTALLER_NAME and the value
337 * is less than 33 characters.
338 *
339 * @throws IllegalArgumentException if {@code key} cannot be used with string value.
340 */
341 public StringAtomicFormula(@Key int key, @NonNull String value) {
342 super(key);
343 checkArgument(
344 key == PACKAGE_NAME
345 || key == APP_CERTIFICATE
346 || key == INSTALLER_CERTIFICATE
347 || key == INSTALLER_NAME,
348 String.format(
349 "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
350 mValue = hashValue(key, value);
351 mIsHashedValue = !mValue.equals(value);
Song Pan097f65d2019-11-10 18:02:52 +0000352 }
Khaled Abdelmohsene959bae2019-10-04 17:01:13 +0100353
Song Pan097f65d2019-11-10 18:02:52 +0000354 StringAtomicFormula(Parcel in) {
355 super(in.readInt());
356 mValue = in.readStringNoHelper();
Khaled Abdelmohsen1efff872019-11-25 16:44:20 +0000357 mIsHashedValue = in.readByte() != 0;
Song Pan097f65d2019-11-10 18:02:52 +0000358 }
359
360 @NonNull
361 public static final Creator<StringAtomicFormula> CREATOR =
362 new Creator<StringAtomicFormula>() {
363 @Override
364 public StringAtomicFormula createFromParcel(Parcel in) {
365 return new StringAtomicFormula(in);
366 }
367
368 @Override
369 public StringAtomicFormula[] newArray(int size) {
370 return new StringAtomicFormula[size];
371 }
372 };
373
374 @Override
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000375 public int getTag() {
376 return IntegrityFormula.STRING_ATOMIC_FORMULA_TAG;
Song Pan097f65d2019-11-10 18:02:52 +0000377 }
378
379 @Override
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000380 public boolean matches(AppInstallMetadata appInstallMetadata) {
381 if (mValue == null || mIsHashedValue == null) {
382 return false;
383 }
384 return getStringMetadataValue(appInstallMetadata, getKey()).equals(mValue);
Song Pan44e5aa52019-11-26 11:38:17 +0000385 }
386
387 @Override
Omer Nebil Yaveroglu84f7c3f2020-01-29 12:18:10 +0000388 public boolean isAppCertificateFormula() {
389 return getKey() == APP_CERTIFICATE;
390 }
391
392 @Override
393 public boolean isInstallerFormula() {
394 return getKey() == INSTALLER_NAME || getKey() == INSTALLER_CERTIFICATE;
395 }
396
397 @Override
Song Pan097f65d2019-11-10 18:02:52 +0000398 public String toString() {
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000399 if (mValue == null || mIsHashedValue == null) {
400 return String.format("(%s)", keyToString(getKey()));
401 }
Song Pan097f65d2019-11-10 18:02:52 +0000402 return String.format("(%s %s %s)", keyToString(getKey()), operatorToString(EQ), mValue);
403 }
404
405 @Override
406 public boolean equals(Object o) {
407 if (this == o) {
408 return true;
409 }
410 if (o == null || getClass() != o.getClass()) {
411 return false;
412 }
413 StringAtomicFormula that = (StringAtomicFormula) o;
414 return getKey() == that.getKey() && Objects.equals(mValue, that.mValue);
415 }
416
417 @Override
418 public int hashCode() {
419 return Objects.hash(getKey(), mValue);
420 }
421
422 @Override
423 public int describeContents() {
424 return 0;
425 }
426
427 @Override
428 public void writeToParcel(@NonNull Parcel dest, int flags) {
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000429 if (mValue == null || mIsHashedValue == null) {
430 throw new IllegalStateException("Cannot write an empty StringAtomicFormula.");
431 }
Song Pan097f65d2019-11-10 18:02:52 +0000432 dest.writeInt(getKey());
433 dest.writeStringNoHelper(mValue);
Khaled Abdelmohsen1efff872019-11-25 16:44:20 +0000434 dest.writeByte((byte) (mIsHashedValue ? 1 : 0));
Song Pan097f65d2019-11-10 18:02:52 +0000435 }
436
Khaled Abdelmohsen0a181cd2019-11-12 12:24:38 +0000437 public String getValue() {
438 return mValue;
439 }
440
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000441 public Boolean getIsHashedValue() {
Khaled Abdelmohsen1efff872019-11-25 16:44:20 +0000442 return mIsHashedValue;
443 }
444
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000445 private static String getStringMetadataValue(
446 AppInstallMetadata appInstallMetadata, int key) {
447 switch (key) {
448 case AtomicFormula.PACKAGE_NAME:
Song Pan097f65d2019-11-10 18:02:52 +0000449 return appInstallMetadata.getPackageName();
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000450 case AtomicFormula.APP_CERTIFICATE:
Song Pan097f65d2019-11-10 18:02:52 +0000451 return appInstallMetadata.getAppCertificate();
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000452 case AtomicFormula.INSTALLER_CERTIFICATE:
Song Pan097f65d2019-11-10 18:02:52 +0000453 return appInstallMetadata.getInstallerCertificate();
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000454 case AtomicFormula.INSTALLER_NAME:
Song Pan097f65d2019-11-10 18:02:52 +0000455 return appInstallMetadata.getInstallerName();
456 default:
457 throw new IllegalStateException(
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000458 "Unexpected key in StringAtomicFormula: " + key);
459 }
460 }
461
462 private static String hashValue(@Key int key, String value) {
463 // Hash the string value unless it is a PACKAGE_NAME or INSTALLER_NAME and the value is
464 // less than 33 characters.
465 if (value.length() <= 32) {
466 if (key == PACKAGE_NAME || key == INSTALLER_NAME) {
467 return value;
468 }
469 }
470 return hash(value);
471 }
472
473 private static String hash(String value) {
474 try {
475 MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
476 byte[] hashBytes = messageDigest.digest(value.getBytes(StandardCharsets.UTF_8));
477 return IntegrityUtils.getHexDigest(hashBytes);
478 } catch (NoSuchAlgorithmException e) {
479 throw new RuntimeException("SHA-256 algorithm not found", e);
Song Pan097f65d2019-11-10 18:02:52 +0000480 }
481 }
Khaled Abdelmohsene959bae2019-10-04 17:01:13 +0100482 }
483
Song Pan097f65d2019-11-10 18:02:52 +0000484 /** An {@link AtomicFormula} with a key and boolean value. */
485 public static final class BooleanAtomicFormula extends AtomicFormula implements Parcelable {
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000486 private final Boolean mValue;
487
488 /**
489 * Constructs an empty {@link BooleanAtomicFormula}. This should only be used as a base.
490 *
491 * <p>An empty formula will always match to false.
492 *
493 * @throws IllegalArgumentException if {@code key} cannot be used with boolean value
494 */
495 public BooleanAtomicFormula(@Key int key) {
496 super(key);
497 checkArgument(
498 key == PRE_INSTALLED,
499 String.format(
500 "Key %s cannot be used with BooleanAtomicFormula", keyToString(key)));
501 mValue = null;
502 }
Song Pan097f65d2019-11-10 18:02:52 +0000503
504 /**
505 * Constructs a new {@link BooleanAtomicFormula}.
506 *
507 * <p>This formula will hold if and only if the corresponding information of an install
508 * specified by {@code key} equals {@code value}.
509 *
Song Pan44e5aa52019-11-26 11:38:17 +0000510 * @throws IllegalArgumentException if {@code key} cannot be used with boolean value
Song Pan097f65d2019-11-10 18:02:52 +0000511 */
512 public BooleanAtomicFormula(@Key int key, boolean value) {
513 super(key);
514 checkArgument(
515 key == PRE_INSTALLED,
516 String.format(
517 "Key %s cannot be used with BooleanAtomicFormula", keyToString(key)));
518 mValue = value;
519 }
520
521 BooleanAtomicFormula(Parcel in) {
522 super(in.readInt());
523 mValue = in.readByte() != 0;
524 }
525
526 @NonNull
527 public static final Creator<BooleanAtomicFormula> CREATOR =
528 new Creator<BooleanAtomicFormula>() {
529 @Override
530 public BooleanAtomicFormula createFromParcel(Parcel in) {
531 return new BooleanAtomicFormula(in);
532 }
533
534 @Override
535 public BooleanAtomicFormula[] newArray(int size) {
536 return new BooleanAtomicFormula[size];
537 }
538 };
539
540 @Override
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000541 public int getTag() {
542 return IntegrityFormula.BOOLEAN_ATOMIC_FORMULA_TAG;
Song Pan097f65d2019-11-10 18:02:52 +0000543 }
544
545 @Override
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000546 public boolean matches(AppInstallMetadata appInstallMetadata) {
547 if (mValue == null) {
548 return false;
549 }
550 return getBooleanMetadataValue(appInstallMetadata, getKey()) == mValue;
Song Pan44e5aa52019-11-26 11:38:17 +0000551 }
552
553 @Override
Omer Nebil Yaveroglu84f7c3f2020-01-29 12:18:10 +0000554 public boolean isAppCertificateFormula() {
555 return false;
556 }
557
558 @Override
559 public boolean isInstallerFormula() {
560 return false;
561 }
562
563 @Override
Song Pan097f65d2019-11-10 18:02:52 +0000564 public String toString() {
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000565 if (mValue == null) {
566 return String.format("(%s)", keyToString(getKey()));
567 }
Song Pan097f65d2019-11-10 18:02:52 +0000568 return String.format("(%s %s %s)", keyToString(getKey()), operatorToString(EQ), mValue);
569 }
570
571 @Override
572 public boolean equals(Object o) {
573 if (this == o) {
574 return true;
575 }
576 if (o == null || getClass() != o.getClass()) {
577 return false;
578 }
579 BooleanAtomicFormula that = (BooleanAtomicFormula) o;
580 return getKey() == that.getKey() && mValue == that.mValue;
581 }
582
583 @Override
584 public int hashCode() {
585 return Objects.hash(getKey(), mValue);
586 }
587
588 @Override
589 public int describeContents() {
590 return 0;
591 }
592
593 @Override
594 public void writeToParcel(@NonNull Parcel dest, int flags) {
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000595 if (mValue == null) {
596 throw new IllegalStateException("Cannot write an empty BooleanAtomicFormula.");
597 }
Song Pan097f65d2019-11-10 18:02:52 +0000598 dest.writeInt(getKey());
599 dest.writeByte((byte) (mValue ? 1 : 0));
600 }
601
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000602 public Boolean getValue() {
Khaled Abdelmohsen0a181cd2019-11-12 12:24:38 +0000603 return mValue;
604 }
605
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000606 private static boolean getBooleanMetadataValue(
607 AppInstallMetadata appInstallMetadata, int key) {
608 switch (key) {
609 case AtomicFormula.PRE_INSTALLED:
Song Pan097f65d2019-11-10 18:02:52 +0000610 return appInstallMetadata.isPreInstalled();
611 default:
612 throw new IllegalStateException(
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000613 "Unexpected key in BooleanAtomicFormula: " + key);
Song Pan097f65d2019-11-10 18:02:52 +0000614 }
615 }
Khaled Abdelmohsene959bae2019-10-04 17:01:13 +0100616 }
617
Song Pan097f65d2019-11-10 18:02:52 +0000618 public int getKey() {
Khaled Abdelmohsene959bae2019-10-04 17:01:13 +0100619 return mKey;
620 }
621
Song Pan44e5aa52019-11-26 11:38:17 +0000622 static String keyToString(int key) {
Khaled Abdelmohsenbe0454d2019-10-07 10:24:18 +0100623 switch (key) {
624 case PACKAGE_NAME:
Song Pan097f65d2019-11-10 18:02:52 +0000625 return "PACKAGE_NAME";
Khaled Abdelmohsenbe0454d2019-10-07 10:24:18 +0100626 case APP_CERTIFICATE:
Song Pan097f65d2019-11-10 18:02:52 +0000627 return "APP_CERTIFICATE";
Khaled Abdelmohsenbe0454d2019-10-07 10:24:18 +0100628 case VERSION_CODE:
Song Pan097f65d2019-11-10 18:02:52 +0000629 return "VERSION_CODE";
630 case INSTALLER_NAME:
631 return "INSTALLER_NAME";
632 case INSTALLER_CERTIFICATE:
633 return "INSTALLER_CERTIFICATE";
634 case PRE_INSTALLED:
635 return "PRE_INSTALLED";
Khaled Abdelmohsenbe0454d2019-10-07 10:24:18 +0100636 default:
Song Pan097f65d2019-11-10 18:02:52 +0000637 throw new IllegalArgumentException("Unknown key " + key);
Khaled Abdelmohsenbe0454d2019-10-07 10:24:18 +0100638 }
Song Pan097f65d2019-11-10 18:02:52 +0000639 }
640
Song Pan44e5aa52019-11-26 11:38:17 +0000641 static String operatorToString(int op) {
Song Pan097f65d2019-11-10 18:02:52 +0000642 switch (op) {
643 case EQ:
644 return "EQ";
Song Pan097f65d2019-11-10 18:02:52 +0000645 case GT:
646 return "GT";
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000647 case GTE:
648 return "GTE";
Song Pan097f65d2019-11-10 18:02:52 +0000649 default:
650 throw new IllegalArgumentException("Unknown operator " + op);
Khaled Abdelmohsenbe0454d2019-10-07 10:24:18 +0100651 }
652 }
Khaled Abdelmohsen5809a5f2019-11-25 18:20:46 +0000653
654 private static boolean isValidKey(int key) {
655 return key == PACKAGE_NAME
656 || key == APP_CERTIFICATE
657 || key == VERSION_CODE
658 || key == INSTALLER_NAME
659 || key == INSTALLER_CERTIFICATE
660 || key == PRE_INSTALLED;
661 }
Khaled Abdelmohsene959bae2019-10-04 17:01:13 +0100662}