blob: 0f4df972ca9526d0ea4d319ef2a3c45bb07749ad [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
19import com.android.internal.util.XmlUtils;
20
21import org.xmlpull.v1.XmlPullParser;
22import org.xmlpull.v1.XmlPullParserException;
23import org.xmlpull.v1.XmlSerializer;
24
25import android.content.ComponentName;
26import android.content.IntentFilter;
27import android.content.pm.ActivityInfo;
28import android.content.pm.ResolveInfo;
29import android.util.Slog;
30
31import java.io.IOException;
32import java.io.PrintWriter;
Michal Karpinski4ff5def2017-07-26 14:49:56 +010033import java.util.ArrayList;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080034import java.util.List;
35
36public class PreferredComponent {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070037 private static final String TAG_SET = "set";
38 private static final String ATTR_ALWAYS = "always"; // boolean
39 private static final String ATTR_MATCH = "match"; // number
40 private static final String ATTR_NAME = "name"; // component name
41 private static final String ATTR_SET = "set"; // number
42
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080043 public final int mMatch;
44 public final ComponentName mComponent;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070045 // Whether this is to be the one that's always chosen. If false, it's the most recently chosen.
46 public boolean mAlways;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080047
Dianne Hackbornf2ac2762014-08-16 11:44:40 -070048 final String[] mSetPackages;
49 final String[] mSetClasses;
50 final String[] mSetComponents;
51 final String mShortComponent;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080052 private String mParseError;
53
54 private final Callbacks mCallbacks;
55
56 public interface Callbacks {
57 public boolean onReadTag(String tagName, XmlPullParser parser)
58 throws XmlPullParserException, IOException;
59 }
60
61 public PreferredComponent(Callbacks callbacks, int match, ComponentName[] set,
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070062 ComponentName component, boolean always) {
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080063 mCallbacks = callbacks;
64 mMatch = match&IntentFilter.MATCH_CATEGORY_MASK;
65 mComponent = component;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070066 mAlways = always;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080067 mShortComponent = component.flattenToShortString();
68 mParseError = null;
69 if (set != null) {
70 final int N = set.length;
71 String[] myPackages = new String[N];
72 String[] myClasses = new String[N];
73 String[] myComponents = new String[N];
74 for (int i=0; i<N; i++) {
75 ComponentName cn = set[i];
76 if (cn == null) {
77 mSetPackages = null;
78 mSetClasses = null;
79 mSetComponents = null;
80 return;
81 }
82 myPackages[i] = cn.getPackageName().intern();
83 myClasses[i] = cn.getClassName().intern();
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -070084 myComponents[i] = cn.flattenToShortString();
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -080085 }
86 mSetPackages = myPackages;
87 mSetClasses = myClasses;
88 mSetComponents = myComponents;
89 } else {
90 mSetPackages = null;
91 mSetClasses = null;
92 mSetComponents = null;
93 }
94 }
95
96 public PreferredComponent(Callbacks callbacks, XmlPullParser parser)
97 throws XmlPullParserException, IOException {
98 mCallbacks = callbacks;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070099 mShortComponent = parser.getAttributeValue(null, ATTR_NAME);
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800100 mComponent = ComponentName.unflattenFromString(mShortComponent);
101 if (mComponent == null) {
102 mParseError = "Bad activity name " + mShortComponent;
103 }
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700104 String matchStr = parser.getAttributeValue(null, ATTR_MATCH);
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800105 mMatch = matchStr != null ? Integer.parseInt(matchStr, 16) : 0;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700106 String setCountStr = parser.getAttributeValue(null, ATTR_SET);
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800107 int setCount = setCountStr != null ? Integer.parseInt(setCountStr) : 0;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700108 String alwaysStr = parser.getAttributeValue(null, ATTR_ALWAYS);
109 mAlways = alwaysStr != null ? Boolean.parseBoolean(alwaysStr) : true;
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800110
111 String[] myPackages = setCount > 0 ? new String[setCount] : null;
112 String[] myClasses = setCount > 0 ? new String[setCount] : null;
113 String[] myComponents = setCount > 0 ? new String[setCount] : null;
114
115 int setPos = 0;
116
117 int outerDepth = parser.getDepth();
118 int type;
119 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
120 && (type != XmlPullParser.END_TAG
121 || parser.getDepth() > outerDepth)) {
122 if (type == XmlPullParser.END_TAG
123 || type == XmlPullParser.TEXT) {
124 continue;
125 }
126
127 String tagName = parser.getName();
128 //Log.i(TAG, "Parse outerDepth=" + outerDepth + " depth="
129 // + parser.getDepth() + " tag=" + tagName);
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700130 if (tagName.equals(TAG_SET)) {
131 String name = parser.getAttributeValue(null, ATTR_NAME);
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800132 if (name == null) {
133 if (mParseError == null) {
134 mParseError = "No name in set tag in preferred activity "
135 + mShortComponent;
136 }
137 } else if (setPos >= setCount) {
138 if (mParseError == null) {
139 mParseError = "Too many set tags in preferred activity "
140 + mShortComponent;
141 }
142 } else {
143 ComponentName cn = ComponentName.unflattenFromString(name);
144 if (cn == null) {
145 if (mParseError == null) {
146 mParseError = "Bad set name " + name + " in preferred activity "
147 + mShortComponent;
148 }
149 } else {
150 myPackages[setPos] = cn.getPackageName();
151 myClasses[setPos] = cn.getClassName();
152 myComponents[setPos] = name;
153 setPos++;
154 }
155 }
156 XmlUtils.skipCurrentTag(parser);
157 } else if (!mCallbacks.onReadTag(tagName, parser)) {
158 Slog.w("PreferredComponent", "Unknown element: " + parser.getName());
159 XmlUtils.skipCurrentTag(parser);
160 }
161 }
162
163 if (setPos != setCount) {
164 if (mParseError == null) {
165 mParseError = "Not enough set tags (expected " + setCount
166 + " but found " + setPos + ") in " + mShortComponent;
167 }
168 }
169
170 mSetPackages = myPackages;
171 mSetClasses = myClasses;
172 mSetComponents = myComponents;
173 }
174
175 public String getParseError() {
176 return mParseError;
177 }
178
Dianne Hackborn8a2ed1d2013-01-29 15:18:29 -0800179 public void writeToXml(XmlSerializer serializer, boolean full) throws IOException {
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800180 final int NS = mSetClasses != null ? mSetClasses.length : 0;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700181 serializer.attribute(null, ATTR_NAME, mShortComponent);
Dianne Hackborn8a2ed1d2013-01-29 15:18:29 -0800182 if (full) {
183 if (mMatch != 0) {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700184 serializer.attribute(null, ATTR_MATCH, Integer.toHexString(mMatch));
Dianne Hackborn8a2ed1d2013-01-29 15:18:29 -0800185 }
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700186 serializer.attribute(null, ATTR_ALWAYS, Boolean.toString(mAlways));
187 serializer.attribute(null, ATTR_SET, Integer.toString(NS));
Dianne Hackborn8a2ed1d2013-01-29 15:18:29 -0800188 for (int s=0; s<NS; s++) {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700189 serializer.startTag(null, TAG_SET);
190 serializer.attribute(null, ATTR_NAME, mSetComponents[s]);
191 serializer.endTag(null, TAG_SET);
Dianne Hackborn8a2ed1d2013-01-29 15:18:29 -0800192 }
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800193 }
194 }
195
Christopher Tate57792912015-01-27 13:47:51 -0800196 public boolean sameSet(List<ResolveInfo> query) {
Dianne Hackbornf2ac2762014-08-16 11:44:40 -0700197 if (mSetPackages == null) {
198 return query == null;
199 }
200 if (query == null) {
201 return false;
202 }
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800203 final int NQ = query.size();
204 final int NS = mSetPackages.length;
Christopher Tate57792912015-01-27 13:47:51 -0800205
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800206 int numMatch = 0;
207 for (int i=0; i<NQ; i++) {
208 ResolveInfo ri = query.get(i);
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800209 ActivityInfo ai = ri.activityInfo;
210 boolean good = false;
211 for (int j=0; j<NS; j++) {
212 if (mSetPackages[j].equals(ai.packageName)
213 && mSetClasses[j].equals(ai.name)) {
214 numMatch++;
215 good = true;
216 break;
217 }
218 }
219 if (!good) return false;
220 }
221 return numMatch == NS;
222 }
223
Dianne Hackbornf2ac2762014-08-16 11:44:40 -0700224 public boolean sameSet(ComponentName[] comps) {
225 if (mSetPackages == null) return false;
226 final int NQ = comps.length;
227 final int NS = mSetPackages.length;
228 int numMatch = 0;
229 for (int i=0; i<NQ; i++) {
230 ComponentName cn = comps[i];
231 boolean good = false;
232 for (int j=0; j<NS; j++) {
233 if (mSetPackages[j].equals(cn.getPackageName())
234 && mSetClasses[j].equals(cn.getClassName())) {
235 numMatch++;
236 good = true;
237 break;
238 }
239 }
240 if (!good) return false;
241 }
242 return numMatch == NS;
243 }
244
Michal Karpinski4ff5def2017-07-26 14:49:56 +0100245 public boolean isSuperset(List<ResolveInfo> query) {
246 if (mSetPackages == null) {
247 return query == null;
248 }
249 if (query == null) {
250 return true;
251 }
252 final int NQ = query.size();
253 final int NS = mSetPackages.length;
254 if (NS < NQ) {
255 return false;
256 }
257 for (int i=0; i<NQ; i++) {
258 ResolveInfo ri = query.get(i);
259 ActivityInfo ai = ri.activityInfo;
260 boolean foundMatch = false;
261 for (int j=0; j<NS; j++) {
262 if (mSetPackages[j].equals(ai.packageName) && mSetClasses[j].equals(ai.name)) {
263 foundMatch = true;
264 break;
265 }
266 }
267 if (!foundMatch) return false;
268 }
269 return true;
270 }
271
272 /** Returns components from mSetPackages that are present in query. */
273 public ComponentName[] discardObsoleteComponents(List<ResolveInfo> query) {
274 if (mSetPackages == null || query == null) {
275 return new ComponentName[0];
276 }
277 final int NQ = query.size();
278 final int NS = mSetPackages.length;
279 ArrayList<ComponentName> aliveComponents = new ArrayList<>();
280 for (int i = 0; i < NQ; i++) {
281 ResolveInfo ri = query.get(i);
282 ActivityInfo ai = ri.activityInfo;
283 for (int j = 0; j < NS; j++) {
284 if (mSetPackages[j].equals(ai.packageName) && mSetClasses[j].equals(ai.name)) {
285 aliveComponents.add(new ComponentName(mSetPackages[j], mSetClasses[j]));
286 break;
287 }
288 }
289 }
290 return aliveComponents.toArray(new ComponentName[aliveComponents.size()]);
291 }
292
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800293 public void dump(PrintWriter out, String prefix, Object ident) {
294 out.print(prefix); out.print(
295 Integer.toHexString(System.identityHashCode(ident)));
296 out.print(' ');
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700297 out.println(mShortComponent);
298 out.print(prefix); out.print(" mMatch=0x");
299 out.print(Integer.toHexString(mMatch));
300 out.print(" mAlways="); out.println(mAlways);
Dianne Hackbornf8b8a3f2011-03-04 00:05:31 -0800301 if (mSetComponents != null) {
302 out.print(prefix); out.println(" Selected from:");
303 for (int i=0; i<mSetComponents.length; i++) {
304 out.print(prefix); out.print(" ");
305 out.println(mSetComponents[i]);
306 }
307 }
308 }
309}