1 /*
2  * Copyright (C) 2022 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 com.android.server.pm.pkg.component;
18 
19 
20 import android.content.pm.parsing.result.ParseInput;
21 import android.content.pm.parsing.result.ParseResult;
22 import android.content.res.Resources;
23 import android.content.res.TypedArray;
24 import android.content.res.XmlResourceParser;
25 import android.os.Build;
26 import android.util.ArraySet;
27 
28 import com.android.internal.R;
29 import com.android.server.SystemConfig;
30 import com.android.server.pm.pkg.parsing.ParsingPackage;
31 
32 import org.xmlpull.v1.XmlPullParser;
33 import org.xmlpull.v1.XmlPullParserException;
34 
35 import java.io.IOException;
36 import java.util.Set;
37 
38 /**
39  * Utility methods for handling the tag {@code <install-constraints/>}
40  *
41  * @hide
42  */
43 public class InstallConstraintsTagParser {
44 
45     private static final String TAG_FINGERPRINT_PREFIX = "fingerprint-prefix";
46 
47     /**
48      * @hide
49      */
parseInstallConstraints( ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)50     public static ParseResult<ParsingPackage> parseInstallConstraints(
51             ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
52             throws XmlPullParserException, IOException {
53         Set<String> allowlist = SystemConfig.getInstance().getInstallConstraintsAllowlist();
54         if (!allowlist.contains(pkg.getPackageName())) {
55             return input.skip("install-constraints cannot be used by this package");
56         }
57 
58         ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, res, parser);
59         if (prefixes.isSuccess()) {
60             if (validateFingerprintPrefixes(prefixes.getResult())) {
61                 return input.success(pkg);
62             } else {
63                 return input.skip(
64                         "Install of this package is restricted on this device; device fingerprint"
65                                 + " does not start with one of the allowed prefixes");
66             }
67         }
68         return input.skip(prefixes.getErrorMessage());
69     }
70 
parseFingerprintPrefixes( ParseInput input, Resources res, XmlResourceParser parser)71     private static ParseResult<Set<String>> parseFingerprintPrefixes(
72             ParseInput input, Resources res, XmlResourceParser parser)
73             throws XmlPullParserException, IOException {
74         Set<String> prefixes = new ArraySet<>();
75         int type;
76         while (true) {
77             // move to the tag that contains the next prefix
78             type = parser.next();
79             if (type == XmlPullParser.END_TAG) {
80                 if (prefixes.size() == 0) {
81                     return input.error("install-constraints must contain at least one constraint");
82                 }
83                 return input.success(prefixes);
84             } else if (type == XmlPullParser.START_TAG) {
85                 if (parser.getName().equals(TAG_FINGERPRINT_PREFIX)) {
86                     ParseResult<String> parsedPrefix =
87                             readFingerprintPrefixValue(input, res, parser);
88                     if (parsedPrefix.isSuccess()) {
89                         prefixes.add(parsedPrefix.getResult());
90                     } else {
91                         return input.error(parsedPrefix.getErrorMessage());
92                     }
93                 } else {
94                     return input.error("Unexpected tag: " + parser.getName());
95                 }
96 
97                 // consume the end tag of this attribute
98                 type = parser.next();
99                 if (type != XmlPullParser.END_TAG) {
100                     return input.error("Expected end tag; instead got " + type);
101                 }
102             }
103         }
104     }
105 
readFingerprintPrefixValue(ParseInput input, Resources res, XmlResourceParser parser)106     private static ParseResult<String> readFingerprintPrefixValue(ParseInput input, Resources res,
107             XmlResourceParser parser) {
108         TypedArray sa = res.obtainAttributes(parser,
109                 R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix);
110         try {
111             String value = sa.getString(
112                     R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix_value);
113             if (value == null) {
114                 return input.error("Failed to specify prefix value");
115             }
116             return input.success(value);
117         } finally {
118             sa.recycle();
119         }
120     }
121 
validateFingerprintPrefixes(Set<String> prefixes)122     private static boolean validateFingerprintPrefixes(Set<String> prefixes) {
123         String fingerprint = Build.FINGERPRINT;
124         for (String prefix : prefixes) {
125             if (fingerprint.startsWith(prefix)) {
126                 return true;
127             }
128         }
129         return false;
130     }
131 }
132