1/*
2 * Copyright (C) 2015 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 android.content.pm;
18
19import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
20import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
21import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
22import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
23import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
24
25import android.os.Parcel;
26import android.os.Parcelable;
27import android.text.TextUtils;
28import android.util.ArraySet;
29import android.util.Log;
30
31import com.android.internal.util.XmlUtils;
32
33import org.xmlpull.v1.XmlPullParser;
34import org.xmlpull.v1.XmlPullParserException;
35import org.xmlpull.v1.XmlSerializer;
36
37import java.io.IOException;
38import java.util.ArrayList;
39
40/**
41 * The {@link com.android.server.pm.PackageManagerService} maintains some
42 * {@link IntentFilterVerificationInfo}s for each domain / package name.
43 *
44 * @hide
45 */
46public final class IntentFilterVerificationInfo implements Parcelable {
47    private static final String TAG = IntentFilterVerificationInfo.class.getName();
48
49    private static final String TAG_DOMAIN = "domain";
50    private static final String ATTR_DOMAIN_NAME = "name";
51    private static final String ATTR_PACKAGE_NAME = "packageName";
52    private static final String ATTR_STATUS = "status";
53
54    private ArraySet<String> mDomains = new ArraySet<>();
55    private String mPackageName;
56    private int mMainStatus;
57
58    public IntentFilterVerificationInfo() {
59        mPackageName = null;
60        mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
61    }
62
63    public IntentFilterVerificationInfo(String packageName, ArrayList<String> domains) {
64        mPackageName = packageName;
65        mDomains.addAll(domains);
66        mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
67    }
68
69    public IntentFilterVerificationInfo(XmlPullParser parser)
70            throws IOException, XmlPullParserException {
71        readFromXml(parser);
72    }
73
74    public IntentFilterVerificationInfo(Parcel source) {
75        readFromParcel(source);
76    }
77
78    public String getPackageName() {
79        return mPackageName;
80    }
81
82    public int getStatus() {
83        return mMainStatus;
84    }
85
86    public void setStatus(int s) {
87        if (s >= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED &&
88                s <= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
89            mMainStatus = s;
90        } else {
91            Log.w(TAG, "Trying to set a non supported status: " + s);
92        }
93    }
94
95    public ArraySet<String> getDomains() {
96        return mDomains;
97    }
98
99    public void setDomains(ArrayList<String> list) {
100        mDomains = new ArraySet<>(list);
101    }
102
103    public String getDomainsString() {
104        StringBuilder sb = new StringBuilder();
105        for (String str : mDomains) {
106            if (sb.length() > 0) {
107                sb.append(" ");
108            }
109            sb.append(str);
110        }
111        return sb.toString();
112    }
113
114    String getStringFromXml(XmlPullParser parser, String attribute, String defaultValue) {
115        String value = parser.getAttributeValue(null, attribute);
116        if (value == null) {
117            String msg = "Missing element under " + TAG +": " + attribute + " at " +
118                    parser.getPositionDescription();
119            Log.w(TAG, msg);
120            return defaultValue;
121        } else {
122            return value;
123        }
124    }
125
126    int getIntFromXml(XmlPullParser parser, String attribute, int defaultValue) {
127        String value = parser.getAttributeValue(null, attribute);
128        if (TextUtils.isEmpty(value)) {
129            String msg = "Missing element under " + TAG +": " + attribute + " at " +
130                    parser.getPositionDescription();
131            Log.w(TAG, msg);
132            return defaultValue;
133        } else {
134            return Integer.parseInt(value);
135        }
136    }
137
138    public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
139            IOException {
140        mPackageName = getStringFromXml(parser, ATTR_PACKAGE_NAME, null);
141        if (mPackageName == null) {
142            Log.e(TAG, "Package name cannot be null!");
143        }
144        int status = getIntFromXml(parser, ATTR_STATUS, -1);
145        if (status == -1) {
146            Log.e(TAG, "Unknown status value: " + status);
147        }
148        mMainStatus = status;
149
150        int outerDepth = parser.getDepth();
151        int type;
152        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
153                && (type != XmlPullParser.END_TAG
154                || parser.getDepth() > outerDepth)) {
155            if (type == XmlPullParser.END_TAG
156                    || type == XmlPullParser.TEXT) {
157                continue;
158            }
159
160            String tagName = parser.getName();
161            if (tagName.equals(TAG_DOMAIN)) {
162                String name = getStringFromXml(parser, ATTR_DOMAIN_NAME, null);
163                if (!TextUtils.isEmpty(name)) {
164                    mDomains.add(name);
165                }
166            } else {
167                Log.w(TAG, "Unknown tag parsing IntentFilter: " + tagName);
168            }
169            XmlUtils.skipCurrentTag(parser);
170        }
171    }
172
173    public void writeToXml(XmlSerializer serializer) throws IOException {
174        serializer.attribute(null, ATTR_PACKAGE_NAME, mPackageName);
175        serializer.attribute(null, ATTR_STATUS, String.valueOf(mMainStatus));
176        for (String str : mDomains) {
177            serializer.startTag(null, TAG_DOMAIN);
178            serializer.attribute(null, ATTR_DOMAIN_NAME, str);
179            serializer.endTag(null, TAG_DOMAIN);
180        }
181    }
182
183    public String getStatusString() {
184        return getStatusStringFromValue(mMainStatus);
185    }
186
187    public static String getStatusStringFromValue(long val) {
188        StringBuilder sb = new StringBuilder();
189        switch ((int)(val >> 32)) {
190            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
191                sb.append("always : ");
192                sb.append(Long.toHexString(val & 0x00000000FFFFFFFF));
193                break;
194
195            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
196                sb.append("ask");
197                break;
198
199            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
200                sb.append("never");
201                break;
202
203            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK:
204                sb.append("always-ask");
205                break;
206
207            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
208            default:
209                sb.append("undefined");
210                break;
211        }
212        return sb.toString();
213    }
214
215    @Override
216    public int describeContents() {
217        return 0;
218    }
219
220    private void readFromParcel(Parcel source) {
221        mPackageName = source.readString();
222        mMainStatus = source.readInt();
223        ArrayList<String> list = new ArrayList<>();
224        source.readStringList(list);
225        mDomains.addAll(list);
226    }
227
228    @Override
229    public void writeToParcel(Parcel dest, int flags) {
230        dest.writeString(mPackageName);
231        dest.writeInt(mMainStatus);
232        dest.writeStringList(new ArrayList<>(mDomains));
233    }
234
235    public static final Creator<IntentFilterVerificationInfo> CREATOR =
236            new Creator<IntentFilterVerificationInfo>() {
237                public IntentFilterVerificationInfo createFromParcel(Parcel source) {
238                    return new IntentFilterVerificationInfo(source);
239                }
240                public IntentFilterVerificationInfo[] newArray(int size) {
241                    return new IntentFilterVerificationInfo[size];
242                }
243            };
244}
245