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