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