1 /*
2  * Copyright 2018 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 package com.android.server.pm;
17 
18 import android.annotation.NonNull;
19 import android.text.TextUtils;
20 
21 import com.android.modules.utils.TypedXmlPullParser;
22 import com.android.modules.utils.TypedXmlSerializer;
23 
24 import org.xmlpull.v1.XmlPullParser;
25 import org.xmlpull.v1.XmlPullParserException;
26 
27 import java.io.IOException;
28 import java.util.ArrayList;
29 
30 /**
31  * Represents a Share Target definition, read from the application's manifest (shortcuts.xml)
32  */
33 class ShareTargetInfo {
34 
35     private static final String TAG_SHARE_TARGET = "share-target";
36     private static final String ATTR_TARGET_CLASS = "targetClass";
37 
38     private static final String TAG_DATA = "data";
39     private static final String ATTR_SCHEME = "scheme";
40     private static final String ATTR_HOST = "host";
41     private static final String ATTR_PORT = "port";
42     private static final String ATTR_PATH = "path";
43     private static final String ATTR_PATH_PATTERN = "pathPattern";
44     private static final String ATTR_PATH_PREFIX = "pathPrefix";
45     private static final String ATTR_MIME_TYPE = "mimeType";
46 
47     private static final String TAG_CATEGORY = "category";
48     private static final String ATTR_NAME = "name";
49 
50     static class TargetData {
51         final String mScheme;
52         final String mHost;
53         final String mPort;
54         final String mPath;
55         final String mPathPattern;
56         final String mPathPrefix;
57         final String mMimeType;
58 
TargetData(String scheme, String host, String port, String path, String pathPattern, String pathPrefix, String mimeType)59         TargetData(String scheme, String host, String port, String path, String pathPattern,
60                 String pathPrefix, String mimeType) {
61             mScheme = scheme;
62             mHost = host;
63             mPort = port;
64             mPath = path;
65             mPathPattern = pathPattern;
66             mPathPrefix = pathPrefix;
67             mMimeType = mimeType;
68         }
69 
toStringInner(StringBuilder strBuilder)70         public void toStringInner(StringBuilder strBuilder) {
71             if (!TextUtils.isEmpty(mScheme)) {
72                 strBuilder.append(" scheme=").append(mScheme);
73             }
74             if (!TextUtils.isEmpty(mHost)) {
75                 strBuilder.append(" host=").append(mHost);
76             }
77             if (!TextUtils.isEmpty(mPort)) {
78                 strBuilder.append(" port=").append(mPort);
79             }
80             if (!TextUtils.isEmpty(mPath)) {
81                 strBuilder.append(" path=").append(mPath);
82             }
83             if (!TextUtils.isEmpty(mPathPattern)) {
84                 strBuilder.append(" pathPattern=").append(mPathPattern);
85             }
86             if (!TextUtils.isEmpty(mPathPrefix)) {
87                 strBuilder.append(" pathPrefix=").append(mPathPrefix);
88             }
89             if (!TextUtils.isEmpty(mMimeType)) {
90                 strBuilder.append(" mimeType=").append(mMimeType);
91             }
92         }
93 
94         @Override
toString()95         public String toString() {
96             StringBuilder strBuilder = new StringBuilder();
97             toStringInner(strBuilder);
98             return strBuilder.toString();
99         }
100     }
101 
102     final TargetData[] mTargetData;
103     final String mTargetClass;
104     final String[] mCategories;
105 
ShareTargetInfo(TargetData[] data, String targetClass, String[] categories)106     ShareTargetInfo(TargetData[] data, String targetClass, String[] categories) {
107         mTargetData = data;
108         mTargetClass = targetClass;
109         mCategories = categories;
110     }
111 
112     @Override
toString()113     public String toString() {
114         StringBuilder strBuilder = new StringBuilder();
115         strBuilder.append("targetClass=").append(mTargetClass);
116         for (int i = 0; i < mTargetData.length; i++) {
117             strBuilder.append(" data={");
118             mTargetData[i].toStringInner(strBuilder);
119             strBuilder.append("}");
120         }
121         for (int i = 0; i < mCategories.length; i++) {
122             strBuilder.append(" category=").append(mCategories[i]);
123         }
124 
125         return strBuilder.toString();
126     }
127 
saveToXml(@onNull TypedXmlSerializer out)128     void saveToXml(@NonNull TypedXmlSerializer out) throws IOException {
129         out.startTag(null, TAG_SHARE_TARGET);
130 
131         ShortcutService.writeAttr(out, ATTR_TARGET_CLASS, mTargetClass);
132 
133         for (int i = 0; i < mTargetData.length; i++) {
134             out.startTag(null, TAG_DATA);
135             ShortcutService.writeAttr(out, ATTR_SCHEME, mTargetData[i].mScheme);
136             ShortcutService.writeAttr(out, ATTR_HOST, mTargetData[i].mHost);
137             ShortcutService.writeAttr(out, ATTR_PORT, mTargetData[i].mPort);
138             ShortcutService.writeAttr(out, ATTR_PATH, mTargetData[i].mPath);
139             ShortcutService.writeAttr(out, ATTR_PATH_PATTERN, mTargetData[i].mPathPattern);
140             ShortcutService.writeAttr(out, ATTR_PATH_PREFIX, mTargetData[i].mPathPrefix);
141             ShortcutService.writeAttr(out, ATTR_MIME_TYPE, mTargetData[i].mMimeType);
142             out.endTag(null, TAG_DATA);
143         }
144 
145         for (int i = 0; i < mCategories.length; i++) {
146             out.startTag(null, TAG_CATEGORY);
147             ShortcutService.writeAttr(out, ATTR_NAME, mCategories[i]);
148             out.endTag(null, TAG_CATEGORY);
149         }
150 
151         out.endTag(null, TAG_SHARE_TARGET);
152     }
153 
loadFromXml(TypedXmlPullParser parser)154     static ShareTargetInfo loadFromXml(TypedXmlPullParser parser)
155             throws IOException, XmlPullParserException {
156         final String targetClass = ShortcutService.parseStringAttribute(parser, ATTR_TARGET_CLASS);
157         final ArrayList<ShareTargetInfo.TargetData> targetData = new ArrayList<>();
158         final ArrayList<String> categories = new ArrayList<>();
159 
160         int type;
161         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
162             if (type == XmlPullParser.START_TAG) {
163                 switch (parser.getName()) {
164                     case TAG_DATA:
165                         targetData.add(parseTargetData(parser));
166                         break;
167                     case TAG_CATEGORY:
168                         categories.add(ShortcutService.parseStringAttribute(parser, ATTR_NAME));
169                         break;
170                 }
171             } else if (type == XmlPullParser.END_TAG && parser.getName().equals(TAG_SHARE_TARGET)) {
172                 break;
173             }
174         }
175         if (targetData.isEmpty() || targetClass == null || categories.isEmpty()) {
176             return null;
177         }
178         return new ShareTargetInfo(
179                 targetData.toArray(new ShareTargetInfo.TargetData[targetData.size()]),
180                 targetClass, categories.toArray(new String[categories.size()]));
181     }
182 
parseTargetData(TypedXmlPullParser parser)183     private static ShareTargetInfo.TargetData parseTargetData(TypedXmlPullParser parser) {
184         final String scheme = ShortcutService.parseStringAttribute(parser, ATTR_SCHEME);
185         final String host = ShortcutService.parseStringAttribute(parser, ATTR_HOST);
186         final String port = ShortcutService.parseStringAttribute(parser, ATTR_PORT);
187         final String path = ShortcutService.parseStringAttribute(parser, ATTR_PATH);
188         final String pathPattern = ShortcutService.parseStringAttribute(parser, ATTR_PATH_PATTERN);
189         final String pathPrefix = ShortcutService.parseStringAttribute(parser, ATTR_PATH_PREFIX);
190         final String mimeType = ShortcutService.parseStringAttribute(parser, ATTR_MIME_TYPE);
191 
192         return new ShareTargetInfo.TargetData(scheme, host, port, path, pathPattern, pathPrefix,
193                 mimeType);
194     }
195 }
196