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