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 com.android.internal.util.XmlUtils;
20 
21 import org.xmlpull.v1.XmlPullParser;
22 import org.xmlpull.v1.XmlPullParserException;
23 import org.xmlpull.v1.XmlSerializer;
24 
25 import android.content.ComponentName;
26 import android.content.IntentFilter;
27 import android.content.pm.ActivityInfo;
28 import android.content.pm.ResolveInfo;
29 import android.util.Slog;
30 
31 import java.io.IOException;
32 import java.io.PrintWriter;
33 import java.util.List;
34 
35 public class PreferredComponent {
36     private static final String TAG_SET = "set";
37     private static final String ATTR_ALWAYS = "always"; // boolean
38     private static final String ATTR_MATCH = "match"; // number
39     private static final String ATTR_NAME = "name"; // component name
40     private static final String ATTR_SET = "set"; // number
41 
42     public final int mMatch;
43     public final ComponentName mComponent;
44     // Whether this is to be the one that's always chosen. If false, it's the most recently chosen.
45     public boolean mAlways;
46 
47     final String[] mSetPackages;
48     final String[] mSetClasses;
49     final String[] mSetComponents;
50     final String mShortComponent;
51     private String mParseError;
52 
53     private final Callbacks mCallbacks;
54 
55     public interface Callbacks {
onReadTag(String tagName, XmlPullParser parser)56         public boolean onReadTag(String tagName, XmlPullParser parser)
57                 throws XmlPullParserException, IOException;
58     }
59 
PreferredComponent(Callbacks callbacks, int match, ComponentName[] set, ComponentName component, boolean always)60     public PreferredComponent(Callbacks callbacks, int match, ComponentName[] set,
61             ComponentName component, boolean always) {
62         mCallbacks = callbacks;
63         mMatch = match&IntentFilter.MATCH_CATEGORY_MASK;
64         mComponent = component;
65         mAlways = always;
66         mShortComponent = component.flattenToShortString();
67         mParseError = null;
68         if (set != null) {
69             final int N = set.length;
70             String[] myPackages = new String[N];
71             String[] myClasses = new String[N];
72             String[] myComponents = new String[N];
73             for (int i=0; i<N; i++) {
74                 ComponentName cn = set[i];
75                 if (cn == null) {
76                     mSetPackages = null;
77                     mSetClasses = null;
78                     mSetComponents = null;
79                     return;
80                 }
81                 myPackages[i] = cn.getPackageName().intern();
82                 myClasses[i] = cn.getClassName().intern();
83                 myComponents[i] = cn.flattenToShortString();
84             }
85             mSetPackages = myPackages;
86             mSetClasses = myClasses;
87             mSetComponents = myComponents;
88         } else {
89             mSetPackages = null;
90             mSetClasses = null;
91             mSetComponents = null;
92         }
93     }
94 
PreferredComponent(Callbacks callbacks, XmlPullParser parser)95     public PreferredComponent(Callbacks callbacks, XmlPullParser parser)
96             throws XmlPullParserException, IOException {
97         mCallbacks = callbacks;
98         mShortComponent = parser.getAttributeValue(null, ATTR_NAME);
99         mComponent = ComponentName.unflattenFromString(mShortComponent);
100         if (mComponent == null) {
101             mParseError = "Bad activity name " + mShortComponent;
102         }
103         String matchStr = parser.getAttributeValue(null, ATTR_MATCH);
104         mMatch = matchStr != null ? Integer.parseInt(matchStr, 16) : 0;
105         String setCountStr = parser.getAttributeValue(null, ATTR_SET);
106         int setCount = setCountStr != null ? Integer.parseInt(setCountStr) : 0;
107         String alwaysStr = parser.getAttributeValue(null, ATTR_ALWAYS);
108         mAlways = alwaysStr != null ? Boolean.parseBoolean(alwaysStr) : true;
109 
110         String[] myPackages = setCount > 0 ? new String[setCount] : null;
111         String[] myClasses = setCount > 0 ? new String[setCount] : null;
112         String[] myComponents = setCount > 0 ? new String[setCount] : null;
113 
114         int setPos = 0;
115 
116         int outerDepth = parser.getDepth();
117         int type;
118         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
119                && (type != XmlPullParser.END_TAG
120                        || parser.getDepth() > outerDepth)) {
121             if (type == XmlPullParser.END_TAG
122                     || type == XmlPullParser.TEXT) {
123                 continue;
124             }
125 
126             String tagName = parser.getName();
127             //Log.i(TAG, "Parse outerDepth=" + outerDepth + " depth="
128             //        + parser.getDepth() + " tag=" + tagName);
129             if (tagName.equals(TAG_SET)) {
130                 String name = parser.getAttributeValue(null, ATTR_NAME);
131                 if (name == null) {
132                     if (mParseError == null) {
133                         mParseError = "No name in set tag in preferred activity "
134                             + mShortComponent;
135                     }
136                 } else if (setPos >= setCount) {
137                     if (mParseError == null) {
138                         mParseError = "Too many set tags in preferred activity "
139                             + mShortComponent;
140                     }
141                 } else {
142                     ComponentName cn = ComponentName.unflattenFromString(name);
143                     if (cn == null) {
144                         if (mParseError == null) {
145                             mParseError = "Bad set name " + name + " in preferred activity "
146                                 + mShortComponent;
147                         }
148                     } else {
149                         myPackages[setPos] = cn.getPackageName();
150                         myClasses[setPos] = cn.getClassName();
151                         myComponents[setPos] = name;
152                         setPos++;
153                     }
154                 }
155                 XmlUtils.skipCurrentTag(parser);
156             } else if (!mCallbacks.onReadTag(tagName, parser)) {
157                 Slog.w("PreferredComponent", "Unknown element: " + parser.getName());
158                 XmlUtils.skipCurrentTag(parser);
159             }
160         }
161 
162         if (setPos != setCount) {
163             if (mParseError == null) {
164                 mParseError = "Not enough set tags (expected " + setCount
165                     + " but found " + setPos + ") in " + mShortComponent;
166             }
167         }
168 
169         mSetPackages = myPackages;
170         mSetClasses = myClasses;
171         mSetComponents = myComponents;
172     }
173 
getParseError()174     public String getParseError() {
175         return mParseError;
176     }
177 
writeToXml(XmlSerializer serializer, boolean full)178     public void writeToXml(XmlSerializer serializer, boolean full) throws IOException {
179         final int NS = mSetClasses != null ? mSetClasses.length : 0;
180         serializer.attribute(null, ATTR_NAME, mShortComponent);
181         if (full) {
182             if (mMatch != 0) {
183                 serializer.attribute(null, ATTR_MATCH, Integer.toHexString(mMatch));
184             }
185             serializer.attribute(null, ATTR_ALWAYS, Boolean.toString(mAlways));
186             serializer.attribute(null, ATTR_SET, Integer.toString(NS));
187             for (int s=0; s<NS; s++) {
188                 serializer.startTag(null, TAG_SET);
189                 serializer.attribute(null, ATTR_NAME, mSetComponents[s]);
190                 serializer.endTag(null, TAG_SET);
191             }
192         }
193     }
194 
sameSet(List<ResolveInfo> query)195     public boolean sameSet(List<ResolveInfo> query) {
196         if (mSetPackages == null) {
197             return query == null;
198         }
199         if (query == null) {
200             return false;
201         }
202         final int NQ = query.size();
203         final int NS = mSetPackages.length;
204 
205         int numMatch = 0;
206         for (int i=0; i<NQ; i++) {
207             ResolveInfo ri = query.get(i);
208             ActivityInfo ai = ri.activityInfo;
209             boolean good = false;
210             for (int j=0; j<NS; j++) {
211                 if (mSetPackages[j].equals(ai.packageName)
212                         && mSetClasses[j].equals(ai.name)) {
213                     numMatch++;
214                     good = true;
215                     break;
216                 }
217             }
218             if (!good) return false;
219         }
220         return numMatch == NS;
221     }
222 
sameSet(ComponentName[] comps)223     public boolean sameSet(ComponentName[] comps) {
224         if (mSetPackages == null) return false;
225         final int NQ = comps.length;
226         final int NS = mSetPackages.length;
227         int numMatch = 0;
228         for (int i=0; i<NQ; i++) {
229             ComponentName cn = comps[i];
230             boolean good = false;
231             for (int j=0; j<NS; j++) {
232                 if (mSetPackages[j].equals(cn.getPackageName())
233                         && mSetClasses[j].equals(cn.getClassName())) {
234                     numMatch++;
235                     good = true;
236                     break;
237                 }
238             }
239             if (!good) return false;
240         }
241         return numMatch == NS;
242     }
243 
dump(PrintWriter out, String prefix, Object ident)244     public void dump(PrintWriter out, String prefix, Object ident) {
245         out.print(prefix); out.print(
246                 Integer.toHexString(System.identityHashCode(ident)));
247                 out.print(' ');
248                 out.println(mShortComponent);
249         out.print(prefix); out.print(" mMatch=0x");
250                 out.print(Integer.toHexString(mMatch));
251                 out.print(" mAlways="); out.println(mAlways);
252         if (mSetComponents != null) {
253             out.print(prefix); out.println("  Selected from:");
254             for (int i=0; i<mSetComponents.length; i++) {
255                 out.print(prefix); out.print("    ");
256                         out.println(mSetComponents[i]);
257             }
258         }
259     }
260 }
261