| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.content.pm.parsing.component; |
| |
| import android.annotation.NonNull; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.PackageParser; |
| import android.content.pm.parsing.ParsingPackage; |
| import android.content.pm.parsing.ParsingPackageUtils; |
| import android.content.pm.parsing.ParsingUtils; |
| import android.content.pm.parsing.result.ParseInput; |
| import android.content.pm.parsing.result.ParseResult; |
| import android.content.res.Resources; |
| import android.content.res.TypedArray; |
| import android.content.res.XmlResourceParser; |
| import android.os.PatternMatcher; |
| import android.text.TextUtils; |
| import android.util.Slog; |
| import android.util.TypedValue; |
| |
| import com.android.internal.R; |
| |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| import java.io.IOException; |
| import java.util.Iterator; |
| |
| /** @hide */ |
| public class ParsedIntentInfoUtils { |
| |
| private static final String TAG = ParsingPackageUtils.TAG; |
| |
| @NonNull |
| public static ParseResult<ParsedIntentInfo> parseIntentInfo(String className, |
| ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs, |
| boolean allowAutoVerify, ParseInput input) |
| throws XmlPullParserException, IOException { |
| ParsedIntentInfo intentInfo = new ParsedIntentInfo(); |
| TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestIntentFilter); |
| try { |
| intentInfo.setPriority(sa.getInt(R.styleable.AndroidManifestIntentFilter_priority, 0)); |
| intentInfo.setOrder(sa.getInt(R.styleable.AndroidManifestIntentFilter_order, 0)); |
| |
| TypedValue v = sa.peekValue(R.styleable.AndroidManifestIntentFilter_label); |
| if (v != null) { |
| intentInfo.labelRes = v.resourceId; |
| if (v.resourceId == 0) { |
| intentInfo.nonLocalizedLabel = v.coerceToString(); |
| } |
| } |
| |
| if (PackageParser.sUseRoundIcon) { |
| intentInfo.icon = sa.getResourceId( |
| R.styleable.AndroidManifestIntentFilter_roundIcon, 0); |
| } |
| |
| if (intentInfo.icon == 0) { |
| intentInfo.icon = sa.getResourceId(R.styleable.AndroidManifestIntentFilter_icon, 0); |
| } |
| |
| if (allowAutoVerify) { |
| intentInfo.setAutoVerify(sa.getBoolean( |
| R.styleable.AndroidManifestIntentFilter_autoVerify, |
| false)); |
| } |
| } finally { |
| sa.recycle(); |
| } |
| final int depth = parser.getDepth(); |
| int type; |
| while ((type = parser.next()) != XmlPullParser.END_DOCUMENT |
| && (type != XmlPullParser.END_TAG |
| || parser.getDepth() > depth)) { |
| if (type != XmlPullParser.START_TAG) { |
| continue; |
| } |
| |
| final ParseResult result; |
| String nodeName = parser.getName(); |
| switch (nodeName) { |
| case "action": { |
| String value = parser.getAttributeValue(PackageParser.ANDROID_RESOURCES, |
| "name"); |
| if (TextUtils.isEmpty(value)) { |
| result = input.error("No value supplied for <android:name>"); |
| } else { |
| intentInfo.addAction(value); |
| result = input.success(null); |
| } |
| break; |
| } |
| case "category": { |
| String value = parser.getAttributeValue(PackageParser.ANDROID_RESOURCES, |
| "name"); |
| if (TextUtils.isEmpty(value)) { |
| result = input.error("No value supplied for <android:name>"); |
| } else { |
| intentInfo.addCategory(value); |
| result = input.success(null); |
| } |
| break; |
| } |
| case "data": |
| result = parseData(intentInfo, res, parser, allowGlobs, input); |
| break; |
| default: |
| result = ParsingUtils.unknownTag("<intent-filter>", pkg, parser, input); |
| break; |
| } |
| |
| if (result.isError()) { |
| return input.error(result); |
| } |
| } |
| |
| intentInfo.hasDefault = intentInfo.hasCategory(Intent.CATEGORY_DEFAULT); |
| |
| if (PackageParser.DEBUG_PARSER) { |
| final StringBuilder cats = new StringBuilder("Intent d="); |
| cats.append(intentInfo.isHasDefault()); |
| cats.append(", cat="); |
| |
| final Iterator<String> it = intentInfo.categoriesIterator(); |
| if (it != null) { |
| while (it.hasNext()) { |
| cats.append(' '); |
| cats.append(it.next()); |
| } |
| } |
| Slog.d(TAG, cats.toString()); |
| } |
| |
| return input.success(intentInfo); |
| } |
| |
| @NonNull |
| private static ParseResult<ParsedIntentInfo> parseData(ParsedIntentInfo intentInfo, |
| Resources resources, XmlResourceParser parser, boolean allowGlobs, ParseInput input) { |
| TypedArray sa = resources.obtainAttributes(parser, R.styleable.AndroidManifestData); |
| try { |
| String str = sa.getNonConfigurationString( |
| R.styleable.AndroidManifestData_mimeType, 0); |
| if (str != null) { |
| try { |
| intentInfo.addDataType(str); |
| } catch (IntentFilter.MalformedMimeTypeException e) { |
| return input.error(e.toString()); |
| } |
| } |
| |
| str = sa.getNonConfigurationString( |
| R.styleable.AndroidManifestData_mimeGroup, 0); |
| if (str != null) { |
| intentInfo.addMimeGroup(str); |
| } |
| |
| str = sa.getNonConfigurationString( |
| R.styleable.AndroidManifestData_scheme, 0); |
| if (str != null) { |
| intentInfo.addDataScheme(str); |
| } |
| |
| str = sa.getNonConfigurationString( |
| R.styleable.AndroidManifestData_ssp, 0); |
| if (str != null) { |
| intentInfo.addDataSchemeSpecificPart(str, |
| PatternMatcher.PATTERN_LITERAL); |
| } |
| |
| str = sa.getNonConfigurationString( |
| R.styleable.AndroidManifestData_sspPrefix, 0); |
| if (str != null) { |
| intentInfo.addDataSchemeSpecificPart(str, |
| PatternMatcher.PATTERN_PREFIX); |
| } |
| |
| str = sa.getNonConfigurationString( |
| R.styleable.AndroidManifestData_sspPattern, 0); |
| if (str != null) { |
| if (!allowGlobs) { |
| return input.error( |
| "sspPattern not allowed here; ssp must be literal"); |
| } |
| intentInfo.addDataSchemeSpecificPart(str, |
| PatternMatcher.PATTERN_SIMPLE_GLOB); |
| } |
| |
| String host = sa.getNonConfigurationString( |
| R.styleable.AndroidManifestData_host, 0); |
| String port = sa.getNonConfigurationString( |
| R.styleable.AndroidManifestData_port, 0); |
| if (host != null) { |
| intentInfo.addDataAuthority(host, port); |
| } |
| |
| str = sa.getNonConfigurationString( |
| R.styleable.AndroidManifestData_path, 0); |
| if (str != null) { |
| intentInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL); |
| } |
| |
| str = sa.getNonConfigurationString( |
| R.styleable.AndroidManifestData_pathPrefix, 0); |
| if (str != null) { |
| intentInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX); |
| } |
| |
| str = sa.getNonConfigurationString( |
| R.styleable.AndroidManifestData_pathPattern, 0); |
| if (str != null) { |
| if (!allowGlobs) { |
| return input.error( |
| "pathPattern not allowed here; path must be literal"); |
| } |
| intentInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB); |
| } |
| |
| str = sa.getNonConfigurationString( |
| R.styleable.AndroidManifestData_pathAdvancedPattern, 0); |
| if (str != null) { |
| if (!allowGlobs) { |
| return input.error( |
| "pathAdvancedPattern not allowed here; path must be literal"); |
| } |
| intentInfo.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB); |
| } |
| |
| return input.success(null); |
| } finally { |
| sa.recycle(); |
| } |
| } |
| } |