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;
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    public final int mMatch;
37    public final ComponentName mComponent;
38
39    private final String[] mSetPackages;
40    private final String[] mSetClasses;
41    private final String[] mSetComponents;
42    private final String mShortComponent;
43    private String mParseError;
44
45    private final Callbacks mCallbacks;
46
47    public interface Callbacks {
48        public boolean onReadTag(String tagName, XmlPullParser parser)
49                throws XmlPullParserException, IOException;
50    }
51
52    public PreferredComponent(Callbacks callbacks, int match, ComponentName[] set,
53            ComponentName component) {
54        mCallbacks = callbacks;
55        mMatch = match&IntentFilter.MATCH_CATEGORY_MASK;
56        mComponent = component;
57        mShortComponent = component.flattenToShortString();
58        mParseError = null;
59        if (set != null) {
60            final int N = set.length;
61            String[] myPackages = new String[N];
62            String[] myClasses = new String[N];
63            String[] myComponents = new String[N];
64            for (int i=0; i<N; i++) {
65                ComponentName cn = set[i];
66                if (cn == null) {
67                    mSetPackages = null;
68                    mSetClasses = null;
69                    mSetComponents = null;
70                    return;
71                }
72                myPackages[i] = cn.getPackageName().intern();
73                myClasses[i] = cn.getClassName().intern();
74                myComponents[i] = cn.flattenToShortString().intern();
75            }
76            mSetPackages = myPackages;
77            mSetClasses = myClasses;
78            mSetComponents = myComponents;
79        } else {
80            mSetPackages = null;
81            mSetClasses = null;
82            mSetComponents = null;
83        }
84    }
85
86    public PreferredComponent(Callbacks callbacks, XmlPullParser parser)
87            throws XmlPullParserException, IOException {
88        mCallbacks = callbacks;
89        mShortComponent = parser.getAttributeValue(null, "name");
90        mComponent = ComponentName.unflattenFromString(mShortComponent);
91        if (mComponent == null) {
92            mParseError = "Bad activity name " + mShortComponent;
93        }
94        String matchStr = parser.getAttributeValue(null, "match");
95        mMatch = matchStr != null ? Integer.parseInt(matchStr, 16) : 0;
96        String setCountStr = parser.getAttributeValue(null, "set");
97        int setCount = setCountStr != null ? Integer.parseInt(setCountStr) : 0;
98
99        String[] myPackages = setCount > 0 ? new String[setCount] : null;
100        String[] myClasses = setCount > 0 ? new String[setCount] : null;
101        String[] myComponents = setCount > 0 ? new String[setCount] : null;
102
103        int setPos = 0;
104
105        int outerDepth = parser.getDepth();
106        int type;
107        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
108               && (type != XmlPullParser.END_TAG
109                       || parser.getDepth() > outerDepth)) {
110            if (type == XmlPullParser.END_TAG
111                    || type == XmlPullParser.TEXT) {
112                continue;
113            }
114
115            String tagName = parser.getName();
116            //Log.i(TAG, "Parse outerDepth=" + outerDepth + " depth="
117            //        + parser.getDepth() + " tag=" + tagName);
118            if (tagName.equals("set")) {
119                String name = parser.getAttributeValue(null, "name");
120                if (name == null) {
121                    if (mParseError == null) {
122                        mParseError = "No name in set tag in preferred activity "
123                            + mShortComponent;
124                    }
125                } else if (setPos >= setCount) {
126                    if (mParseError == null) {
127                        mParseError = "Too many set tags in preferred activity "
128                            + mShortComponent;
129                    }
130                } else {
131                    ComponentName cn = ComponentName.unflattenFromString(name);
132                    if (cn == null) {
133                        if (mParseError == null) {
134                            mParseError = "Bad set name " + name + " in preferred activity "
135                                + mShortComponent;
136                        }
137                    } else {
138                        myPackages[setPos] = cn.getPackageName();
139                        myClasses[setPos] = cn.getClassName();
140                        myComponents[setPos] = name;
141                        setPos++;
142                    }
143                }
144                XmlUtils.skipCurrentTag(parser);
145            } else if (!mCallbacks.onReadTag(tagName, parser)) {
146                Slog.w("PreferredComponent", "Unknown element: " + parser.getName());
147                XmlUtils.skipCurrentTag(parser);
148            }
149        }
150
151        if (setPos != setCount) {
152            if (mParseError == null) {
153                mParseError = "Not enough set tags (expected " + setCount
154                    + " but found " + setPos + ") in " + mShortComponent;
155            }
156        }
157
158        mSetPackages = myPackages;
159        mSetClasses = myClasses;
160        mSetComponents = myComponents;
161    }
162
163    public String getParseError() {
164        return mParseError;
165    }
166
167    public void writeToXml(XmlSerializer serializer) throws IOException {
168        final int NS = mSetClasses != null ? mSetClasses.length : 0;
169        serializer.attribute(null, "name", mShortComponent);
170        if (mMatch != 0) {
171            serializer.attribute(null, "match", Integer.toHexString(mMatch));
172        }
173        serializer.attribute(null, "set", Integer.toString(NS));
174        for (int s=0; s<NS; s++) {
175            serializer.startTag(null, "set");
176            serializer.attribute(null, "name", mSetComponents[s]);
177            serializer.endTag(null, "set");
178        }
179    }
180
181    public boolean sameSet(List<ResolveInfo> query, int priority) {
182        if (mSetPackages == null) return false;
183        final int NQ = query.size();
184        final int NS = mSetPackages.length;
185        int numMatch = 0;
186        for (int i=0; i<NQ; i++) {
187            ResolveInfo ri = query.get(i);
188            if (ri.priority != priority) continue;
189            ActivityInfo ai = ri.activityInfo;
190            boolean good = false;
191            for (int j=0; j<NS; j++) {
192                if (mSetPackages[j].equals(ai.packageName)
193                        && mSetClasses[j].equals(ai.name)) {
194                    numMatch++;
195                    good = true;
196                    break;
197                }
198            }
199            if (!good) return false;
200        }
201        return numMatch == NS;
202    }
203
204    public void dump(PrintWriter out, String prefix, Object ident) {
205        out.print(prefix); out.print(
206                Integer.toHexString(System.identityHashCode(ident)));
207                out.print(' ');
208                out.print(mComponent.flattenToShortString());
209                out.print(" match=0x");
210                out.println( Integer.toHexString(mMatch));
211        if (mSetComponents != null) {
212            out.print(prefix); out.println("  Selected from:");
213            for (int i=0; i<mSetComponents.length; i++) {
214                out.print(prefix); out.print("    ");
215                        out.println(mSetComponents[i]);
216            }
217        }
218    }
219}
220