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