blob: c312fc0eca2fb02dab080e6fe08d23bf13b06d28 [file] [log] [blame]
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -08001/*
2 * Copyright (C) 2011 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
Amith Yamasani09e9cdc2013-11-06 14:54:50 -080017package com.android.server.pm;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080018
Henry Liudeb09222018-04-20 15:48:30 +080019import android.content.pm.PackageManagerInternal;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080020import com.android.internal.util.XmlUtils;
21
Henry Liudeb09222018-04-20 15:48:30 +080022import com.android.server.LocalServices;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080023import org.xmlpull.v1.XmlPullParser;
24import org.xmlpull.v1.XmlPullParserException;
25import org.xmlpull.v1.XmlSerializer;
26
27import android.content.ComponentName;
28import android.content.IntentFilter;
29import android.content.pm.ActivityInfo;
30import android.content.pm.ResolveInfo;
31import android.util.Slog;
32
33import java.io.IOException;
34import java.io.PrintWriter;
Michal Karpinski4ff5def2017-07-26 14:49:56 +010035import java.util.ArrayList;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080036import java.util.List;
37
38public class PreferredComponent {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070039 private static final String TAG_SET = "set";
40 private static final String ATTR_ALWAYS = "always"; // boolean
41 private static final String ATTR_MATCH = "match"; // number
42 private static final String ATTR_NAME = "name"; // component name
43 private static final String ATTR_SET = "set"; // number
44
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080045 public final int mMatch;
46 public final ComponentName mComponent;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070047 // Whether this is to be the one that's always chosen. If false, it's the most recently chosen.
48 public boolean mAlways;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080049
Dianne Hackbornf2ac2762014-08-16 11:44:40 -070050 final String[] mSetPackages;
51 final String[] mSetClasses;
52 final String[] mSetComponents;
53 final String mShortComponent;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080054 private String mParseError;
55
56 private final Callbacks mCallbacks;
Henry Liudeb09222018-04-20 15:48:30 +080057 private final String mSetupWizardPackageName;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080058
59 public interface Callbacks {
60 public boolean onReadTag(String tagName, XmlPullParser parser)
61 throws XmlPullParserException, IOException;
62 }
63
64 public PreferredComponent(Callbacks callbacks, int match, ComponentName[] set,
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070065 ComponentName component, boolean always) {
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080066 mCallbacks = callbacks;
67 mMatch = match&IntentFilter.MATCH_CATEGORY_MASK;
68 mComponent = component;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070069 mAlways = always;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080070 mShortComponent = component.flattenToShortString();
71 mParseError = null;
Henry Liudeb09222018-04-20 15:48:30 +080072 mSetupWizardPackageName = null;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080073 if (set != null) {
74 final int N = set.length;
75 String[] myPackages = new String[N];
76 String[] myClasses = new String[N];
77 String[] myComponents = new String[N];
78 for (int i=0; i<N; i++) {
79 ComponentName cn = set[i];
80 if (cn == null) {
81 mSetPackages = null;
82 mSetClasses = null;
83 mSetComponents = null;
84 return;
85 }
86 myPackages[i] = cn.getPackageName().intern();
87 myClasses[i] = cn.getClassName().intern();
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -070088 myComponents[i] = cn.flattenToShortString();
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080089 }
90 mSetPackages = myPackages;
91 mSetClasses = myClasses;
92 mSetComponents = myComponents;
93 } else {
94 mSetPackages = null;
95 mSetClasses = null;
96 mSetComponents = null;
97 }
98 }
99
100 public PreferredComponent(Callbacks callbacks, XmlPullParser parser)
101 throws XmlPullParserException, IOException {
102 mCallbacks = callbacks;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700103 mShortComponent = parser.getAttributeValue(null, ATTR_NAME);
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800104 mComponent = ComponentName.unflattenFromString(mShortComponent);
105 if (mComponent == null) {
106 mParseError = "Bad activity name " + mShortComponent;
107 }
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700108 String matchStr = parser.getAttributeValue(null, ATTR_MATCH);
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800109 mMatch = matchStr != null ? Integer.parseInt(matchStr, 16) : 0;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700110 String setCountStr = parser.getAttributeValue(null, ATTR_SET);
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800111 int setCount = setCountStr != null ? Integer.parseInt(setCountStr) : 0;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700112 String alwaysStr = parser.getAttributeValue(null, ATTR_ALWAYS);
113 mAlways = alwaysStr != null ? Boolean.parseBoolean(alwaysStr) : true;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800114
115 String[] myPackages = setCount > 0 ? new String[setCount] : null;
116 String[] myClasses = setCount > 0 ? new String[setCount] : null;
117 String[] myComponents = setCount > 0 ? new String[setCount] : null;
118
119 int setPos = 0;
120
121 int outerDepth = parser.getDepth();
122 int type;
123 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
124 && (type != XmlPullParser.END_TAG
125 || parser.getDepth() > outerDepth)) {
126 if (type == XmlPullParser.END_TAG
127 || type == XmlPullParser.TEXT) {
128 continue;
129 }
130
131 String tagName = parser.getName();
132 //Log.i(TAG, "Parse outerDepth=" + outerDepth + " depth="
133 // + parser.getDepth() + " tag=" + tagName);
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700134 if (tagName.equals(TAG_SET)) {
135 String name = parser.getAttributeValue(null, ATTR_NAME);
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800136 if (name == null) {
137 if (mParseError == null) {
138 mParseError = "No name in set tag in preferred activity "
139 + mShortComponent;
140 }
141 } else if (setPos >= setCount) {
142 if (mParseError == null) {
143 mParseError = "Too many set tags in preferred activity "
144 + mShortComponent;
145 }
146 } else {
147 ComponentName cn = ComponentName.unflattenFromString(name);
148 if (cn == null) {
149 if (mParseError == null) {
150 mParseError = "Bad set name " + name + " in preferred activity "
151 + mShortComponent;
152 }
153 } else {
154 myPackages[setPos] = cn.getPackageName();
155 myClasses[setPos] = cn.getClassName();
156 myComponents[setPos] = name;
157 setPos++;
158 }
159 }
160 XmlUtils.skipCurrentTag(parser);
161 } else if (!mCallbacks.onReadTag(tagName, parser)) {
162 Slog.w("PreferredComponent", "Unknown element: " + parser.getName());
163 XmlUtils.skipCurrentTag(parser);
164 }
165 }
166
167 if (setPos != setCount) {
168 if (mParseError == null) {
169 mParseError = "Not enough set tags (expected " + setCount
170 + " but found " + setPos + ") in " + mShortComponent;
171 }
172 }
173
174 mSetPackages = myPackages;
175 mSetClasses = myClasses;
176 mSetComponents = myComponents;
Henry Liudeb09222018-04-20 15:48:30 +0800177 final PackageManagerInternal packageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
178 mSetupWizardPackageName = packageManagerInternal.getSetupWizardPackageName();
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800179 }
180
181 public String getParseError() {
182 return mParseError;
183 }
184
Dianne Hackborn8a2ed1d2013-01-29 15:18:29 -0800185 public void writeToXml(XmlSerializer serializer, boolean full) throws IOException {
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800186 final int NS = mSetClasses != null ? mSetClasses.length : 0;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700187 serializer.attribute(null, ATTR_NAME, mShortComponent);
Dianne Hackborn8a2ed1d2013-01-29 15:18:29 -0800188 if (full) {
189 if (mMatch != 0) {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700190 serializer.attribute(null, ATTR_MATCH, Integer.toHexString(mMatch));
Dianne Hackborn8a2ed1d2013-01-29 15:18:29 -0800191 }
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700192 serializer.attribute(null, ATTR_ALWAYS, Boolean.toString(mAlways));
193 serializer.attribute(null, ATTR_SET, Integer.toString(NS));
Dianne Hackborn8a2ed1d2013-01-29 15:18:29 -0800194 for (int s=0; s<NS; s++) {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700195 serializer.startTag(null, TAG_SET);
196 serializer.attribute(null, ATTR_NAME, mSetComponents[s]);
197 serializer.endTag(null, TAG_SET);
Dianne Hackborn8a2ed1d2013-01-29 15:18:29 -0800198 }
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800199 }
200 }
201
Henry Liudeb09222018-04-20 15:48:30 +0800202 public boolean sameSet(List<ResolveInfo> query, boolean excludeSetupWizardPackage) {
Dianne Hackbornf2ac2762014-08-16 11:44:40 -0700203 if (mSetPackages == null) {
204 return query == null;
205 }
206 if (query == null) {
207 return false;
208 }
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800209 final int NQ = query.size();
210 final int NS = mSetPackages.length;
211 int numMatch = 0;
212 for (int i=0; i<NQ; i++) {
213 ResolveInfo ri = query.get(i);
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800214 ActivityInfo ai = ri.activityInfo;
215 boolean good = false;
Henry Liudeb09222018-04-20 15:48:30 +0800216
217 // ignore SetupWizard package's launcher capability because it is only existed
218 // during SetupWizard is running
219 if (excludeSetupWizardPackage && ai.packageName.equals(mSetupWizardPackageName)) {
220 continue;
221 }
222
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800223 for (int j=0; j<NS; j++) {
224 if (mSetPackages[j].equals(ai.packageName)
225 && mSetClasses[j].equals(ai.name)) {
226 numMatch++;
227 good = true;
228 break;
229 }
230 }
231 if (!good) return false;
232 }
233 return numMatch == NS;
234 }
235
Dianne Hackbornf2ac2762014-08-16 11:44:40 -0700236 public boolean sameSet(ComponentName[] comps) {
237 if (mSetPackages == null) return false;
238 final int NQ = comps.length;
239 final int NS = mSetPackages.length;
240 int numMatch = 0;
241 for (int i=0; i<NQ; i++) {
242 ComponentName cn = comps[i];
243 boolean good = false;
244 for (int j=0; j<NS; j++) {
245 if (mSetPackages[j].equals(cn.getPackageName())
246 && mSetClasses[j].equals(cn.getClassName())) {
247 numMatch++;
248 good = true;
249 break;
250 }
251 }
252 if (!good) return false;
253 }
254 return numMatch == NS;
255 }
256
Henry Liudeb09222018-04-20 15:48:30 +0800257 public boolean isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage) {
Michal Karpinski4ff5def2017-07-26 14:49:56 +0100258 if (mSetPackages == null) {
259 return query == null;
260 }
261 if (query == null) {
262 return true;
263 }
264 final int NQ = query.size();
265 final int NS = mSetPackages.length;
Henry Liudeb09222018-04-20 15:48:30 +0800266 if (!excludeSetupWizardPackage && NS < NQ) {
Michal Karpinski4ff5def2017-07-26 14:49:56 +0100267 return false;
268 }
269 for (int i=0; i<NQ; i++) {
270 ResolveInfo ri = query.get(i);
271 ActivityInfo ai = ri.activityInfo;
272 boolean foundMatch = false;
Henry Liudeb09222018-04-20 15:48:30 +0800273
274 // ignore SetupWizard package's launcher capability because it is only existed
275 // during SetupWizard is running
276 if (excludeSetupWizardPackage && ai.packageName.equals(mSetupWizardPackageName)) {
277 continue;
278 }
279
Michal Karpinski4ff5def2017-07-26 14:49:56 +0100280 for (int j=0; j<NS; j++) {
281 if (mSetPackages[j].equals(ai.packageName) && mSetClasses[j].equals(ai.name)) {
282 foundMatch = true;
283 break;
284 }
285 }
286 if (!foundMatch) return false;
287 }
288 return true;
289 }
290
291 /** Returns components from mSetPackages that are present in query. */
292 public ComponentName[] discardObsoleteComponents(List<ResolveInfo> query) {
293 if (mSetPackages == null || query == null) {
294 return new ComponentName[0];
295 }
296 final int NQ = query.size();
297 final int NS = mSetPackages.length;
298 ArrayList<ComponentName> aliveComponents = new ArrayList<>();
299 for (int i = 0; i < NQ; i++) {
300 ResolveInfo ri = query.get(i);
301 ActivityInfo ai = ri.activityInfo;
302 for (int j = 0; j < NS; j++) {
303 if (mSetPackages[j].equals(ai.packageName) && mSetClasses[j].equals(ai.name)) {
304 aliveComponents.add(new ComponentName(mSetPackages[j], mSetClasses[j]));
305 break;
306 }
307 }
308 }
309 return aliveComponents.toArray(new ComponentName[aliveComponents.size()]);
310 }
311
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800312 public void dump(PrintWriter out, String prefix, Object ident) {
313 out.print(prefix); out.print(
314 Integer.toHexString(System.identityHashCode(ident)));
315 out.print(' ');
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700316 out.println(mShortComponent);
317 out.print(prefix); out.print(" mMatch=0x");
318 out.print(Integer.toHexString(mMatch));
319 out.print(" mAlways="); out.println(mAlways);
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800320 if (mSetComponents != null) {
321 out.print(prefix); out.println(" Selected from:");
322 for (int i=0; i<mSetComponents.length; i++) {
323 out.print(prefix); out.print(" ");
324 out.println(mSetComponents[i]);
325 }
326 }
327 }
328}