PreferredComponent.java revision 4ff5defa5328411035183b785a77db41778bc272
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
17package com.android.server.pm;
18
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;
33import java.util.ArrayList;
34import java.util.List;
35
36public class PreferredComponent {
37    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
43    public final int mMatch;
44    public final ComponentName mComponent;
45    // Whether this is to be the one that's always chosen. If false, it's the most recently chosen.
46    public boolean mAlways;
47
48    final String[] mSetPackages;
49    final String[] mSetClasses;
50    final String[] mSetComponents;
51    final String mShortComponent;
52    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,
62            ComponentName component, boolean always) {
63        mCallbacks = callbacks;
64        mMatch = match&IntentFilter.MATCH_CATEGORY_MASK;
65        mComponent = component;
66        mAlways = always;
67        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();
84                myComponents[i] = cn.flattenToShortString();
85            }
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;
99        mShortComponent = parser.getAttributeValue(null, ATTR_NAME);
100        mComponent = ComponentName.unflattenFromString(mShortComponent);
101        if (mComponent == null) {
102            mParseError = "Bad activity name " + mShortComponent;
103        }
104        String matchStr = parser.getAttributeValue(null, ATTR_MATCH);
105        mMatch = matchStr != null ? Integer.parseInt(matchStr, 16) : 0;
106        String setCountStr = parser.getAttributeValue(null, ATTR_SET);
107        int setCount = setCountStr != null ? Integer.parseInt(setCountStr) : 0;
108        String alwaysStr = parser.getAttributeValue(null, ATTR_ALWAYS);
109        mAlways = alwaysStr != null ? Boolean.parseBoolean(alwaysStr) : true;
110
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);
130            if (tagName.equals(TAG_SET)) {
131                String name = parser.getAttributeValue(null, ATTR_NAME);
132                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
179    public void writeToXml(XmlSerializer serializer, boolean full) throws IOException {
180        final int NS = mSetClasses != null ? mSetClasses.length : 0;
181        serializer.attribute(null, ATTR_NAME, mShortComponent);
182        if (full) {
183            if (mMatch != 0) {
184                serializer.attribute(null, ATTR_MATCH, Integer.toHexString(mMatch));
185            }
186            serializer.attribute(null, ATTR_ALWAYS, Boolean.toString(mAlways));
187            serializer.attribute(null, ATTR_SET, Integer.toString(NS));
188            for (int s=0; s<NS; s++) {
189                serializer.startTag(null, TAG_SET);
190                serializer.attribute(null, ATTR_NAME, mSetComponents[s]);
191                serializer.endTag(null, TAG_SET);
192            }
193        }
194    }
195
196    public boolean sameSet(List<ResolveInfo> query) {
197        if (mSetPackages == null) {
198            return query == null;
199        }
200        if (query == null) {
201            return false;
202        }
203        final int NQ = query.size();
204        final int NS = mSetPackages.length;
205
206        int numMatch = 0;
207        for (int i=0; i<NQ; i++) {
208            ResolveInfo ri = query.get(i);
209            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
224    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
245    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
293    public void dump(PrintWriter out, String prefix, Object ident) {
294        out.print(prefix); out.print(
295                Integer.toHexString(System.identityHashCode(ident)));
296                out.print(' ');
297                out.println(mShortComponent);
298        out.print(prefix); out.print(" mMatch=0x");
299                out.print(Integer.toHexString(mMatch));
300                out.print(" mAlways="); out.println(mAlways);
301        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}
310