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.pm.PackageManagerInternal;
20 import com.android.internal.util.XmlUtils;
21 
22 import com.android.server.LocalServices;
23 import org.xmlpull.v1.XmlPullParser;
24 import org.xmlpull.v1.XmlPullParserException;
25 import org.xmlpull.v1.XmlSerializer;
26 
27 import android.content.ComponentName;
28 import android.content.IntentFilter;
29 import android.content.pm.ActivityInfo;
30 import android.content.pm.ResolveInfo;
31 import android.util.Slog;
32 
33 import java.io.IOException;
34 import java.io.PrintWriter;
35 import java.util.ArrayList;
36 import java.util.List;
37 
38 public class PreferredComponent {
39     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 
45     public final int mMatch;
46     public final ComponentName mComponent;
47     // Whether this is to be the one that's always chosen. If false, it's the most recently chosen.
48     public boolean mAlways;
49 
50     final String[] mSetPackages;
51     final String[] mSetClasses;
52     final String[] mSetComponents;
53     final String mShortComponent;
54     private String mParseError;
55 
56     private final Callbacks mCallbacks;
57     private final String mSetupWizardPackageName;
58 
59     public interface Callbacks {
onReadTag(String tagName, XmlPullParser parser)60         public boolean onReadTag(String tagName, XmlPullParser parser)
61                 throws XmlPullParserException, IOException;
62     }
63 
PreferredComponent(Callbacks callbacks, int match, ComponentName[] set, ComponentName component, boolean always)64     public PreferredComponent(Callbacks callbacks, int match, ComponentName[] set,
65             ComponentName component, boolean always) {
66         mCallbacks = callbacks;
67         mMatch = match&IntentFilter.MATCH_CATEGORY_MASK;
68         mComponent = component;
69         mAlways = always;
70         mShortComponent = component.flattenToShortString();
71         mParseError = null;
72         mSetupWizardPackageName = null;
73         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();
88                 myComponents[i] = cn.flattenToShortString();
89             }
90             mSetPackages = myPackages;
91             mSetClasses = myClasses;
92             mSetComponents = myComponents;
93         } else {
94             mSetPackages = null;
95             mSetClasses = null;
96             mSetComponents = null;
97         }
98     }
99 
PreferredComponent(Callbacks callbacks, XmlPullParser parser)100     public PreferredComponent(Callbacks callbacks, XmlPullParser parser)
101             throws XmlPullParserException, IOException {
102         mCallbacks = callbacks;
103         mShortComponent = parser.getAttributeValue(null, ATTR_NAME);
104         mComponent = ComponentName.unflattenFromString(mShortComponent);
105         if (mComponent == null) {
106             mParseError = "Bad activity name " + mShortComponent;
107         }
108         String matchStr = parser.getAttributeValue(null, ATTR_MATCH);
109         mMatch = matchStr != null ? Integer.parseInt(matchStr, 16) : 0;
110         String setCountStr = parser.getAttributeValue(null, ATTR_SET);
111         int setCount = setCountStr != null ? Integer.parseInt(setCountStr) : 0;
112         String alwaysStr = parser.getAttributeValue(null, ATTR_ALWAYS);
113         mAlways = alwaysStr != null ? Boolean.parseBoolean(alwaysStr) : true;
114 
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);
134             if (tagName.equals(TAG_SET)) {
135                 String name = parser.getAttributeValue(null, ATTR_NAME);
136                 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;
177         final PackageManagerInternal packageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
178         mSetupWizardPackageName = packageManagerInternal.getSetupWizardPackageName();
179     }
180 
getParseError()181     public String getParseError() {
182         return mParseError;
183     }
184 
writeToXml(XmlSerializer serializer, boolean full)185     public void writeToXml(XmlSerializer serializer, boolean full) throws IOException {
186         final int NS = mSetClasses != null ? mSetClasses.length : 0;
187         serializer.attribute(null, ATTR_NAME, mShortComponent);
188         if (full) {
189             if (mMatch != 0) {
190                 serializer.attribute(null, ATTR_MATCH, Integer.toHexString(mMatch));
191             }
192             serializer.attribute(null, ATTR_ALWAYS, Boolean.toString(mAlways));
193             serializer.attribute(null, ATTR_SET, Integer.toString(NS));
194             for (int s=0; s<NS; s++) {
195                 serializer.startTag(null, TAG_SET);
196                 serializer.attribute(null, ATTR_NAME, mSetComponents[s]);
197                 serializer.endTag(null, TAG_SET);
198             }
199         }
200     }
201 
sameSet(List<ResolveInfo> query, boolean excludeSetupWizardPackage)202     public boolean sameSet(List<ResolveInfo> query, boolean excludeSetupWizardPackage) {
203         if (mSetPackages == null) {
204             return query == null;
205         }
206         if (query == null) {
207             return false;
208         }
209         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);
214             ActivityInfo ai = ri.activityInfo;
215             boolean good = false;
216 
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 
223             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 
sameSet(ComponentName[] comps)236     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 
isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage)257     public boolean isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage) {
258         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;
266         if (!excludeSetupWizardPackage && NS < NQ) {
267             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;
273 
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 
280             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. */
discardObsoleteComponents(List<ResolveInfo> 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 
dump(PrintWriter out, String prefix, Object ident)312     public void dump(PrintWriter out, String prefix, Object ident) {
313         out.print(prefix); out.print(
314                 Integer.toHexString(System.identityHashCode(ident)));
315                 out.print(' ');
316                 out.println(mShortComponent);
317         out.print(prefix); out.print(" mMatch=0x");
318                 out.print(Integer.toHexString(mMatch));
319                 out.print(" mAlways="); out.println(mAlways);
320         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 }
329