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.internal.pm.pkg.component;
18 
19 import android.annotation.AttrRes;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.pm.parsing.FrameworkParsingPackageUtils;
25 import android.content.pm.parsing.result.ParseInput;
26 import android.content.pm.parsing.result.ParseResult;
27 import android.content.res.Resources;
28 import android.content.res.TypedArray;
29 import android.content.res.XmlResourceParser;
30 import android.text.TextUtils;
31 
32 import com.android.internal.pm.pkg.parsing.ParsingPackage;
33 import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
34 import com.android.internal.pm.pkg.parsing.ParsingUtils;
35 
36 import org.xmlpull.v1.XmlPullParser;
37 import org.xmlpull.v1.XmlPullParserException;
38 
39 import java.io.IOException;
40 
41 /**
42  * @hide
43  */
44 public class ComponentParseUtils {
45 
isImplicitlyExposedIntent(ParsedIntentInfo intentInfo)46     public static boolean isImplicitlyExposedIntent(ParsedIntentInfo intentInfo) {
47         IntentFilter intentFilter = intentInfo.getIntentFilter();
48         return intentFilter.hasCategory(Intent.CATEGORY_BROWSABLE)
49                 || intentFilter.hasAction(Intent.ACTION_SEND)
50                 || intentFilter.hasAction(Intent.ACTION_SENDTO)
51                 || intentFilter.hasAction(Intent.ACTION_SEND_MULTIPLE);
52     }
53 
parseAllMetaData( ParsingPackage pkg, Resources res, XmlResourceParser parser, String tag, Component component, ParseInput input)54     static <Component extends ParsedComponentImpl> ParseResult<Component> parseAllMetaData(
55             ParsingPackage pkg, Resources res, XmlResourceParser parser, String tag,
56             Component component, ParseInput input) throws XmlPullParserException, IOException {
57         final int depth = parser.getDepth();
58         int type;
59         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
60                 && (type != XmlPullParser.END_TAG || parser.getDepth() > depth)) {
61             if (type != XmlPullParser.START_TAG) {
62                 continue;
63             }
64             if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
65                 continue;
66             }
67 
68             final ParseResult result;
69             if ("meta-data".equals(parser.getName())) {
70                 result = ParsedComponentUtils.addMetaData(component, pkg, res, parser, input);
71             } else {
72                 result = ParsingUtils.unknownTag(tag, pkg, parser, input);
73             }
74 
75             if (result.isError()) {
76                 return input.error(result);
77             }
78         }
79 
80         return input.success(component);
81     }
82 
83     @NonNull
buildProcessName(@onNull String pkg, String defProc, CharSequence procSeq, int flags, String[] separateProcesses, ParseInput input)84     public static ParseResult<String> buildProcessName(@NonNull String pkg, String defProc,
85             CharSequence procSeq, int flags, String[] separateProcesses, ParseInput input) {
86         if ((flags & ParsingPackageUtils.PARSE_IGNORE_PROCESSES) != 0 && !"system".contentEquals(
87                 procSeq)) {
88             return input.success(defProc != null ? defProc : pkg);
89         }
90         if (separateProcesses != null) {
91             for (int i = separateProcesses.length - 1; i >= 0; i--) {
92                 String sp = separateProcesses[i];
93                 if (sp.equals(pkg) || sp.equals(defProc) || sp.contentEquals(procSeq)) {
94                     return input.success(pkg);
95                 }
96             }
97         }
98         if (procSeq == null || procSeq.length() <= 0) {
99             return input.success(defProc);
100         }
101 
102         ParseResult<String> nameResult = ComponentParseUtils.buildCompoundName(pkg, procSeq,
103                 "process", input);
104         return input.success(TextUtils.safeIntern(nameResult.getResult()));
105     }
106 
107     @NonNull
buildTaskAffinityName(String pkg, String defProc, CharSequence procSeq, ParseInput input)108     public static ParseResult<String> buildTaskAffinityName(String pkg, String defProc,
109             CharSequence procSeq, ParseInput input) {
110         if (procSeq == null) {
111             return input.success(defProc);
112         }
113         if (procSeq.length() <= 0) {
114             return input.success(null);
115         }
116         return buildCompoundName(pkg, procSeq, "taskAffinity", input);
117     }
118 
buildCompoundName(String pkg, CharSequence procSeq, String type, ParseInput input)119     public static ParseResult<String> buildCompoundName(String pkg, CharSequence procSeq,
120             String type, ParseInput input) {
121         String proc = procSeq.toString();
122         char c = proc.charAt(0);
123         if (pkg != null && c == ':') {
124             if (proc.length() < 2) {
125                 return input.error("Bad " + type + " name " + proc + " in package " + pkg
126                         + ": must be at least two characters");
127             }
128             String subName = proc.substring(1);
129             final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input,
130                     subName, false, false);
131             if (nameResult.isError()) {
132                 return input.error("Invalid " + type + " name " + proc + " in package " + pkg
133                         + ": " + nameResult.getErrorMessage());
134             }
135             return input.success(pkg + proc);
136         }
137         if (!"system".equals(proc)) {
138             final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input, proc,
139                     true, false);
140             if (nameResult.isError()) {
141                 return input.error("Invalid " + type + " name " + proc + " in package " + pkg
142                         + ": " + nameResult.getErrorMessage());
143             }
144         }
145         return input.success(proc);
146     }
147 
flag(int flag, @AttrRes int attribute, TypedArray typedArray)148     public static int flag(int flag, @AttrRes int attribute, TypedArray typedArray) {
149         return typedArray.getBoolean(attribute, false) ? flag : 0;
150     }
151 
flag(int flag, @AttrRes int attribute, boolean defaultValue, TypedArray typedArray)152     public static int flag(int flag, @AttrRes int attribute, boolean defaultValue,
153             TypedArray typedArray) {
154         return typedArray.getBoolean(attribute, defaultValue) ? flag : 0;
155     }
156 
157     /**
158      * This is not state aware. Avoid and access through PackageInfoUtils in the system server.
159      */
160     @Nullable
getNonLocalizedLabel( ParsedComponent component)161     public static CharSequence getNonLocalizedLabel(
162             ParsedComponent component) {
163         return component.getNonLocalizedLabel();
164     }
165 
166     /**
167      * This is not state aware. Avoid and access through PackageInfoUtils in the system server.
168      * <p>
169      * This is a method of the utility class to discourage use.
170      */
getIcon(ParsedComponent component)171     public static int getIcon(ParsedComponent component) {
172         return component.getIcon();
173     }
174 }
175