• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.pm.parsing.component;
18 
19 import android.annotation.NonNull;
20 import android.content.Intent;
21 import android.content.IntentFilter;
22 import android.content.pm.PackageParser;
23 import android.content.pm.parsing.ParsingPackage;
24 import android.content.pm.parsing.ParsingPackageUtils;
25 import android.content.pm.parsing.ParsingUtils;
26 import android.content.pm.parsing.result.ParseInput;
27 import android.content.pm.parsing.result.ParseResult;
28 import android.content.res.Resources;
29 import android.content.res.TypedArray;
30 import android.content.res.XmlResourceParser;
31 import android.os.PatternMatcher;
32 import android.util.Slog;
33 import android.util.TypedValue;
34 
35 import com.android.internal.R;
36 
37 import org.xmlpull.v1.XmlPullParser;
38 import org.xmlpull.v1.XmlPullParserException;
39 
40 import java.io.IOException;
41 import java.util.Iterator;
42 
43 /** @hide */
44 public class ParsedIntentInfoUtils {
45 
46     private static final String TAG = ParsingUtils.TAG;
47 
48     public static final boolean DEBUG = false;
49 
50     @NonNull
parseIntentInfo(String className, ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs, boolean allowAutoVerify, ParseInput input)51     public static ParseResult<ParsedIntentInfo> parseIntentInfo(String className,
52             ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs,
53             boolean allowAutoVerify, ParseInput input)
54             throws XmlPullParserException, IOException {
55         ParsedIntentInfo intentInfo = new ParsedIntentInfo();
56         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestIntentFilter);
57         try {
58             intentInfo.setPriority(sa.getInt(R.styleable.AndroidManifestIntentFilter_priority, 0));
59             intentInfo.setOrder(sa.getInt(R.styleable.AndroidManifestIntentFilter_order, 0));
60 
61             TypedValue v = sa.peekValue(R.styleable.AndroidManifestIntentFilter_label);
62             if (v != null) {
63                 intentInfo.labelRes = v.resourceId;
64                 if (v.resourceId == 0) {
65                     intentInfo.nonLocalizedLabel = v.coerceToString();
66                 }
67             }
68 
69             if (ParsingPackageUtils.sUseRoundIcon) {
70                 intentInfo.icon = sa.getResourceId(
71                         R.styleable.AndroidManifestIntentFilter_roundIcon, 0);
72             }
73 
74             if (intentInfo.icon == 0) {
75                 intentInfo.icon = sa.getResourceId(R.styleable.AndroidManifestIntentFilter_icon, 0);
76             }
77 
78             if (allowAutoVerify) {
79                 intentInfo.setAutoVerify(sa.getBoolean(
80                         R.styleable.AndroidManifestIntentFilter_autoVerify,
81                         false));
82             }
83         } finally {
84             sa.recycle();
85         }
86         final int depth = parser.getDepth();
87         int type;
88         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
89                 && (type != XmlPullParser.END_TAG
90                 || parser.getDepth() > depth)) {
91             if (type != XmlPullParser.START_TAG) {
92                 continue;
93             }
94 
95             final ParseResult result;
96             String nodeName = parser.getName();
97             switch (nodeName) {
98                 case "action": {
99                     String value = parser.getAttributeValue(PackageParser.ANDROID_RESOURCES,
100                             "name");
101                     if (value == null) {
102                         result = input.error("No value supplied for <android:name>");
103                     } else if (value.isEmpty()) {
104                         intentInfo.addAction(value);
105                         // Prior to R, this was not a failure
106                         result = input.deferError("No value supplied for <android:name>",
107                                 ParseInput.DeferredError.EMPTY_INTENT_ACTION_CATEGORY);
108                     } else {
109                         intentInfo.addAction(value);
110                         result = input.success(null);
111                     }
112                     break;
113                 }
114                 case "category": {
115                     String value = parser.getAttributeValue(PackageParser.ANDROID_RESOURCES,
116                             "name");
117                     if (value == null) {
118                         result = input.error("No value supplied for <android:name>");
119                     } else if (value.isEmpty()) {
120                         intentInfo.addCategory(value);
121                         // Prior to R, this was not a failure
122                         result = input.deferError("No value supplied for <android:name>",
123                                 ParseInput.DeferredError.EMPTY_INTENT_ACTION_CATEGORY);
124                     } else {
125                         intentInfo.addCategory(value);
126                         result = input.success(null);
127                     }
128                     break;
129                 }
130                 case "data":
131                     result = parseData(intentInfo, res, parser, allowGlobs, input);
132                     break;
133                 default:
134                     result = ParsingUtils.unknownTag("<intent-filter>", pkg, parser, input);
135                     break;
136             }
137 
138             if (result.isError()) {
139                 return input.error(result);
140             }
141         }
142 
143         intentInfo.hasDefault = intentInfo.hasCategory(Intent.CATEGORY_DEFAULT);
144 
145         if (DEBUG) {
146             final StringBuilder cats = new StringBuilder("Intent d=");
147             cats.append(intentInfo.isHasDefault());
148             cats.append(", cat=");
149 
150             final Iterator<String> it = intentInfo.categoriesIterator();
151             if (it != null) {
152                 while (it.hasNext()) {
153                     cats.append(' ');
154                     cats.append(it.next());
155                 }
156             }
157             Slog.d(TAG, cats.toString());
158         }
159 
160         return input.success(intentInfo);
161     }
162 
163     @NonNull
parseData(ParsedIntentInfo intentInfo, Resources resources, XmlResourceParser parser, boolean allowGlobs, ParseInput input)164     private static ParseResult<ParsedIntentInfo> parseData(ParsedIntentInfo intentInfo,
165             Resources resources, XmlResourceParser parser, boolean allowGlobs, ParseInput input) {
166         TypedArray sa = resources.obtainAttributes(parser, R.styleable.AndroidManifestData);
167         try {
168             String str = sa.getNonConfigurationString(
169                     R.styleable.AndroidManifestData_mimeType, 0);
170             if (str != null) {
171                 try {
172                     intentInfo.addDataType(str);
173                 } catch (IntentFilter.MalformedMimeTypeException e) {
174                     return input.error(e.toString());
175                 }
176             }
177 
178             str = sa.getNonConfigurationString(
179                     R.styleable.AndroidManifestData_mimeGroup, 0);
180             if (str != null) {
181                 intentInfo.addMimeGroup(str);
182             }
183 
184             str = sa.getNonConfigurationString(
185                     R.styleable.AndroidManifestData_scheme, 0);
186             if (str != null) {
187                 intentInfo.addDataScheme(str);
188             }
189 
190             str = sa.getNonConfigurationString(
191                     R.styleable.AndroidManifestData_ssp, 0);
192             if (str != null) {
193                 intentInfo.addDataSchemeSpecificPart(str,
194                         PatternMatcher.PATTERN_LITERAL);
195             }
196 
197             str = sa.getNonConfigurationString(
198                     R.styleable.AndroidManifestData_sspPrefix, 0);
199             if (str != null) {
200                 intentInfo.addDataSchemeSpecificPart(str,
201                         PatternMatcher.PATTERN_PREFIX);
202             }
203 
204             str = sa.getNonConfigurationString(
205                     R.styleable.AndroidManifestData_sspPattern, 0);
206             if (str != null) {
207                 if (!allowGlobs) {
208                     return input.error(
209                             "sspPattern not allowed here; ssp must be literal");
210                 }
211                 intentInfo.addDataSchemeSpecificPart(str,
212                         PatternMatcher.PATTERN_SIMPLE_GLOB);
213             }
214 
215             str = sa.getNonConfigurationString(
216                     R.styleable.AndroidManifestData_sspAdvancedPattern, 0);
217             if (str != null) {
218                 if (!allowGlobs) {
219                     return input.error(
220                             "sspAdvancedPattern not allowed here; ssp must be literal");
221                 }
222                 intentInfo.addDataSchemeSpecificPart(str,
223                         PatternMatcher.PATTERN_ADVANCED_GLOB);
224             }
225 
226             str = sa.getNonConfigurationString(
227                     R.styleable.AndroidManifestData_sspSuffix, 0);
228             if (str != null) {
229                 intentInfo.addDataSchemeSpecificPart(str,
230                         PatternMatcher.PATTERN_SUFFIX);
231             }
232 
233 
234             String host = sa.getNonConfigurationString(
235                     R.styleable.AndroidManifestData_host, 0);
236             String port = sa.getNonConfigurationString(
237                     R.styleable.AndroidManifestData_port, 0);
238             if (host != null) {
239                 intentInfo.addDataAuthority(host, port);
240             }
241 
242             str = sa.getNonConfigurationString(
243                     R.styleable.AndroidManifestData_path, 0);
244             if (str != null) {
245                 intentInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL);
246             }
247 
248             str = sa.getNonConfigurationString(
249                     R.styleable.AndroidManifestData_pathPrefix, 0);
250             if (str != null) {
251                 intentInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX);
252             }
253 
254             str = sa.getNonConfigurationString(
255                     R.styleable.AndroidManifestData_pathPattern, 0);
256             if (str != null) {
257                 if (!allowGlobs) {
258                     return input.error(
259                             "pathPattern not allowed here; path must be literal");
260                 }
261                 intentInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
262             }
263 
264             str = sa.getNonConfigurationString(
265                     R.styleable.AndroidManifestData_pathAdvancedPattern, 0);
266             if (str != null) {
267                 if (!allowGlobs) {
268                     return input.error(
269                             "pathAdvancedPattern not allowed here; path must be literal");
270                 }
271                 intentInfo.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
272             }
273 
274             str = sa.getNonConfigurationString(
275                     R.styleable.AndroidManifestData_pathSuffix, 0);
276             if (str != null) {
277                 intentInfo.addDataPath(str, PatternMatcher.PATTERN_SUFFIX);
278             }
279 
280 
281             return input.success(null);
282         } finally {
283             sa.recycle();
284         }
285     }
286 }
287