• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.server.pm;
18 
19 import android.content.ComponentName;
20 import android.content.IntentFilter;
21 import android.content.pm.ActivityInfo;
22 import android.content.pm.PackageManagerInternal;
23 import android.content.pm.ResolveInfo;
24 import android.util.Slog;
25 import android.util.TypedXmlPullParser;
26 import android.util.TypedXmlSerializer;
27 
28 import com.android.internal.util.XmlUtils;
29 import com.android.server.LocalServices;
30 
31 import org.xmlpull.v1.XmlPullParser;
32 import org.xmlpull.v1.XmlPullParserException;
33 
34 import java.io.IOException;
35 import java.io.PrintWriter;
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 public class PreferredComponent {
40     private static final String TAG_SET = "set";
41     private static final String ATTR_ALWAYS = "always"; // boolean
42     private static final String ATTR_MATCH = "match"; // number
43     private static final String ATTR_NAME = "name"; // component name
44     private static final String ATTR_SET = "set"; // number
45 
46     public final int mMatch;
47     public final ComponentName mComponent;
48     // Whether this is to be the one that's always chosen. If false, it's the most recently chosen.
49     public boolean mAlways;
50 
51     final String[] mSetPackages;
52     final String[] mSetClasses;
53     final String[] mSetComponents;
54     final String mShortComponent;
55     private String mParseError;
56 
57     private final Callbacks mCallbacks;
58     private final String mSetupWizardPackageName;
59 
60     public interface Callbacks {
onReadTag(String tagName, TypedXmlPullParser parser)61         public boolean onReadTag(String tagName, TypedXmlPullParser parser)
62                 throws XmlPullParserException, IOException;
63     }
64 
PreferredComponent(Callbacks callbacks, int match, ComponentName[] set, ComponentName component, boolean always)65     public PreferredComponent(Callbacks callbacks, int match, ComponentName[] set,
66             ComponentName component, boolean always) {
67         mCallbacks = callbacks;
68         mMatch = match&IntentFilter.MATCH_CATEGORY_MASK;
69         mComponent = component;
70         mAlways = always;
71         mShortComponent = component.flattenToShortString();
72         mParseError = null;
73         mSetupWizardPackageName = null;
74         if (set != null) {
75             final int N = set.length;
76             String[] myPackages = new String[N];
77             String[] myClasses = new String[N];
78             String[] myComponents = new String[N];
79             for (int i=0; i<N; i++) {
80                 ComponentName cn = set[i];
81                 if (cn == null) {
82                     mSetPackages = null;
83                     mSetClasses = null;
84                     mSetComponents = null;
85                     return;
86                 }
87                 myPackages[i] = cn.getPackageName().intern();
88                 myClasses[i] = cn.getClassName().intern();
89                 myComponents[i] = cn.flattenToShortString();
90             }
91             mSetPackages = myPackages;
92             mSetClasses = myClasses;
93             mSetComponents = myComponents;
94         } else {
95             mSetPackages = null;
96             mSetClasses = null;
97             mSetComponents = null;
98         }
99     }
100 
PreferredComponent(Callbacks callbacks, TypedXmlPullParser parser)101     public PreferredComponent(Callbacks callbacks, TypedXmlPullParser parser)
102             throws XmlPullParserException, IOException {
103         mCallbacks = callbacks;
104         mShortComponent = parser.getAttributeValue(null, ATTR_NAME);
105         mComponent = ComponentName.unflattenFromString(mShortComponent);
106         if (mComponent == null) {
107             mParseError = "Bad activity name " + mShortComponent;
108         }
109         mMatch = parser.getAttributeIntHex(null, ATTR_MATCH, 0);
110         int setCount = parser.getAttributeInt(null, ATTR_SET, 0);
111         mAlways = parser.getAttributeBoolean(null, ATTR_ALWAYS, true);
112 
113         String[] myPackages = setCount > 0 ? new String[setCount] : null;
114         String[] myClasses = setCount > 0 ? new String[setCount] : null;
115         String[] myComponents = setCount > 0 ? new String[setCount] : null;
116 
117         int setPos = 0;
118 
119         int outerDepth = parser.getDepth();
120         int type;
121         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
122                && (type != XmlPullParser.END_TAG
123                        || parser.getDepth() > outerDepth)) {
124             if (type == XmlPullParser.END_TAG
125                     || type == XmlPullParser.TEXT) {
126                 continue;
127             }
128 
129             String tagName = parser.getName();
130             //Log.i(TAG, "Parse outerDepth=" + outerDepth + " depth="
131             //        + parser.getDepth() + " tag=" + tagName);
132             if (tagName.equals(TAG_SET)) {
133                 String name = parser.getAttributeValue(null, ATTR_NAME);
134                 if (name == null) {
135                     if (mParseError == null) {
136                         mParseError = "No name in set tag in preferred activity "
137                             + mShortComponent;
138                     }
139                 } else if (setPos >= setCount) {
140                     if (mParseError == null) {
141                         mParseError = "Too many set tags in preferred activity "
142                             + mShortComponent;
143                     }
144                 } else {
145                     ComponentName cn = ComponentName.unflattenFromString(name);
146                     if (cn == null) {
147                         if (mParseError == null) {
148                             mParseError = "Bad set name " + name + " in preferred activity "
149                                 + mShortComponent;
150                         }
151                     } else {
152                         myPackages[setPos] = cn.getPackageName();
153                         myClasses[setPos] = cn.getClassName();
154                         myComponents[setPos] = name;
155                         setPos++;
156                     }
157                 }
158                 XmlUtils.skipCurrentTag(parser);
159             } else if (!mCallbacks.onReadTag(tagName, parser)) {
160                 Slog.w("PreferredComponent", "Unknown element: " + parser.getName());
161                 XmlUtils.skipCurrentTag(parser);
162             }
163         }
164 
165         if (setPos != setCount) {
166             if (mParseError == null) {
167                 mParseError = "Not enough set tags (expected " + setCount
168                     + " but found " + setPos + ") in " + mShortComponent;
169             }
170         }
171 
172         mSetPackages = myPackages;
173         mSetClasses = myClasses;
174         mSetComponents = myComponents;
175         final PackageManagerInternal packageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
176         mSetupWizardPackageName = packageManagerInternal.getSetupWizardPackageName();
177     }
178 
getParseError()179     public String getParseError() {
180         return mParseError;
181     }
182 
writeToXml(TypedXmlSerializer serializer, boolean full)183     public void writeToXml(TypedXmlSerializer serializer, boolean full) throws IOException {
184         final int NS = mSetClasses != null ? mSetClasses.length : 0;
185         serializer.attribute(null, ATTR_NAME, mShortComponent);
186         if (full) {
187             if (mMatch != 0) {
188                 serializer.attributeIntHex(null, ATTR_MATCH, mMatch);
189             }
190             serializer.attributeBoolean(null, ATTR_ALWAYS, mAlways);
191             serializer.attributeInt(null, ATTR_SET, NS);
192             for (int s=0; s<NS; s++) {
193                 serializer.startTag(null, TAG_SET);
194                 serializer.attribute(null, ATTR_NAME, mSetComponents[s]);
195                 serializer.endTag(null, TAG_SET);
196             }
197         }
198     }
199 
sameSet(List<ResolveInfo> query, boolean excludeSetupWizardPackage)200     public boolean sameSet(List<ResolveInfo> query, boolean excludeSetupWizardPackage) {
201         if (mSetPackages == null) {
202             return query == null;
203         }
204         if (query == null) {
205             return false;
206         }
207         final int NQ = query.size();
208         final int NS = mSetPackages.length;
209         int numMatch = 0;
210         for (int i=0; i<NQ; i++) {
211             ResolveInfo ri = query.get(i);
212             ActivityInfo ai = ri.activityInfo;
213             boolean good = false;
214 
215             // ignore SetupWizard package's launcher capability because it is only existed
216             // during SetupWizard is running
217             if (excludeSetupWizardPackage && ai.packageName.equals(mSetupWizardPackageName)) {
218                 continue;
219             }
220 
221             for (int j=0; j<NS; j++) {
222                 if (mSetPackages[j].equals(ai.packageName)
223                         && mSetClasses[j].equals(ai.name)) {
224                     numMatch++;
225                     good = true;
226                     break;
227                 }
228             }
229             if (!good) return false;
230         }
231         return numMatch == NS;
232     }
233 
sameSet(ComponentName[] comps)234     public boolean sameSet(ComponentName[] comps) {
235         if (mSetPackages == null) return false;
236         final int NQ = comps.length;
237         final int NS = mSetPackages.length;
238         int numMatch = 0;
239         for (int i=0; i<NQ; i++) {
240             ComponentName cn = comps[i];
241             boolean good = false;
242             for (int j=0; j<NS; j++) {
243                 if (mSetPackages[j].equals(cn.getPackageName())
244                         && mSetClasses[j].equals(cn.getClassName())) {
245                     numMatch++;
246                     good = true;
247                     break;
248                 }
249             }
250             if (!good) return false;
251         }
252         return numMatch == NS;
253     }
254 
sameSet(PreferredComponent pc)255     public boolean sameSet(PreferredComponent pc) {
256         if (mSetPackages == null || pc == null || pc.mSetPackages == null
257                 || !sameComponent(pc.mComponent)) {
258             return false;
259         }
260         final int otherPackageCount = pc.mSetPackages.length;
261         final int packageCount = mSetPackages.length;
262         if (otherPackageCount != packageCount) {
263             return false;
264         }
265         for (int i = 0; i < packageCount; i++) {
266             if (!mSetPackages[i].equals(pc.mSetPackages[i])
267                     || !mSetClasses[i].equals(pc.mSetClasses[i])) {
268                 return false;
269             }
270         }
271         return true;
272     }
273 
274     /** Returns true if the preferred component represents the provided ComponentName. */
sameComponent(ComponentName comp)275     private boolean sameComponent(ComponentName comp) {
276         if (mComponent == null || comp == null) {
277             return false;
278         }
279         if (mComponent.getPackageName().equals(comp.getPackageName())
280                 && mComponent.getClassName().equals(comp.getClassName())) {
281             return true;
282         }
283         return false;
284     }
285 
isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage)286     public boolean isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage) {
287         if (mSetPackages == null) {
288             return query == null;
289         }
290         if (query == null) {
291             return true;
292         }
293         final int NQ = query.size();
294         final int NS = mSetPackages.length;
295         if (!excludeSetupWizardPackage && NS < NQ) {
296             return false;
297         }
298         for (int i=0; i<NQ; i++) {
299             ResolveInfo ri = query.get(i);
300             ActivityInfo ai = ri.activityInfo;
301             boolean foundMatch = false;
302 
303             // ignore SetupWizard package's launcher capability because it is only existed
304             // during SetupWizard is running
305             if (excludeSetupWizardPackage && ai.packageName.equals(mSetupWizardPackageName)) {
306                 continue;
307             }
308 
309             for (int j=0; j<NS; j++) {
310                 if (mSetPackages[j].equals(ai.packageName) && mSetClasses[j].equals(ai.name)) {
311                     foundMatch = true;
312                     break;
313                 }
314             }
315             if (!foundMatch) return false;
316         }
317         return true;
318     }
319 
320     /** Returns components from mSetPackages that are present in query. */
discardObsoleteComponents(List<ResolveInfo> query)321     public ComponentName[] discardObsoleteComponents(List<ResolveInfo> query) {
322         if (mSetPackages == null || query == null) {
323             return new ComponentName[0];
324         }
325         final int NQ = query.size();
326         final int NS = mSetPackages.length;
327         ArrayList<ComponentName> aliveComponents = new ArrayList<>();
328         for (int i = 0; i < NQ; i++) {
329             ResolveInfo ri = query.get(i);
330             ActivityInfo ai = ri.activityInfo;
331             for (int j = 0; j < NS; j++) {
332                 if (mSetPackages[j].equals(ai.packageName) && mSetClasses[j].equals(ai.name)) {
333                     aliveComponents.add(new ComponentName(mSetPackages[j], mSetClasses[j]));
334                     break;
335                 }
336             }
337         }
338         return aliveComponents.toArray(new ComponentName[aliveComponents.size()]);
339     }
340 
dump(PrintWriter out, String prefix, Object ident)341     public void dump(PrintWriter out, String prefix, Object ident) {
342         out.print(prefix); out.print(
343                 Integer.toHexString(System.identityHashCode(ident)));
344                 out.print(' ');
345                 out.println(mShortComponent);
346         out.print(prefix); out.print(" mMatch=0x");
347                 out.print(Integer.toHexString(mMatch));
348                 out.print(" mAlways="); out.println(mAlways);
349         if (mSetComponents != null) {
350             out.print(prefix); out.println("  Selected from:");
351             for (int i=0; i<mSetComponents.length; i++) {
352                 out.print(prefix); out.print("    ");
353                         out.println(mSetComponents[i]);
354             }
355         }
356     }
357 }
358