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.List;
34
35public 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 {
56        public boolean onReadTag(String tagName, XmlPullParser parser)
57                throws XmlPullParserException, IOException;
58    }
59
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
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
174    public String getParseError() {
175        return mParseError;
176    }
177
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
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
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
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