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