1 /* 2 * Copyright (C) 2022 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 com.android.internal.pm.pkg.component; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.pm.parsing.result.ParseInput; 22 import android.content.pm.parsing.result.ParseResult; 23 import android.content.res.Resources; 24 import android.content.res.TypedArray; 25 import android.content.res.XmlResourceParser; 26 import android.util.ArraySet; 27 28 import com.android.internal.R; 29 30 import org.xmlpull.v1.XmlPullParser; 31 import org.xmlpull.v1.XmlPullParserException; 32 33 import java.io.IOException; 34 import java.util.ArrayList; 35 import java.util.Collections; 36 import java.util.List; 37 38 /** @hide */ 39 public class ParsedAttributionUtils { 40 41 @NonNull parseAttribution(Resources res, XmlResourceParser parser, ParseInput input)42 public static ParseResult<ParsedAttribution> parseAttribution(Resources res, 43 XmlResourceParser parser, ParseInput input) 44 throws IOException, XmlPullParserException { 45 String attributionTag; 46 int label; 47 List<String> inheritFrom = null; 48 49 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAttribution); 50 if (sa == null) { 51 return input.error("<attribution> could not be parsed"); 52 } 53 54 try { 55 attributionTag = sa.getNonConfigurationString( 56 R.styleable.AndroidManifestAttribution_tag, 0); 57 if (attributionTag == null) { 58 return input.error("<attribution> does not specify android:tag"); 59 } 60 if (attributionTag.length() > ParsedAttribution.MAX_ATTRIBUTION_TAG_LEN) { 61 return input.error("android:tag is too long. Max length is " 62 + ParsedAttribution.MAX_ATTRIBUTION_TAG_LEN); 63 } 64 65 label = sa.getResourceId(R.styleable.AndroidManifestAttribution_label, 0); 66 if (label == Resources.ID_NULL) { 67 return input.error("<attribution> does not specify android:label"); 68 } 69 } finally { 70 sa.recycle(); 71 } 72 73 int type; 74 final int innerDepth = parser.getDepth(); 75 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 76 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { 77 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 78 continue; 79 } 80 81 String tagName = parser.getName(); 82 if (tagName.equals("inherit-from")) { 83 sa = res.obtainAttributes(parser, 84 R.styleable.AndroidManifestAttributionInheritFrom); 85 if (sa == null) { 86 return input.error("<inherit-from> could not be parsed"); 87 } 88 89 try { 90 String inheritFromId = sa.getNonConfigurationString( 91 R.styleable.AndroidManifestAttributionInheritFrom_tag, 0); 92 93 if (inheritFrom == null) { 94 inheritFrom = new ArrayList<>(); 95 } 96 inheritFrom.add(inheritFromId); 97 } finally { 98 sa.recycle(); 99 } 100 } else { 101 return input.error("Bad element under <attribution>: " + tagName); 102 } 103 } 104 105 if (inheritFrom == null) { 106 inheritFrom = Collections.emptyList(); 107 } else { 108 ((ArrayList) inheritFrom).trimToSize(); 109 } 110 111 return input.success(new ParsedAttributionImpl(attributionTag, label, inheritFrom)); 112 } 113 114 /** 115 * @return Is this set of attributions a valid combination for a single package? 116 */ isCombinationValid(@ullable List<ParsedAttribution> attributions)117 public static boolean isCombinationValid(@Nullable List<ParsedAttribution> attributions) { 118 if (attributions == null) { 119 return true; 120 } 121 122 ArraySet<String> attributionTags = new ArraySet<>(attributions.size()); 123 ArraySet<String> inheritFromAttributionTags = new ArraySet<>(); 124 125 int numAttributions = attributions.size(); 126 if (numAttributions > ParsedAttributionImpl.MAX_NUM_ATTRIBUTIONS) { 127 return false; 128 } 129 130 for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) { 131 boolean wasAdded = attributionTags.add(attributions.get(attributionNum).getTag()); 132 if (!wasAdded) { 133 // feature id is not unique 134 return false; 135 } 136 } 137 138 for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) { 139 ParsedAttribution feature = attributions.get(attributionNum); 140 141 final List<String> inheritFromList = feature.getInheritFrom(); 142 int numInheritFrom = inheritFromList.size(); 143 for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) { 144 String inheritFrom = inheritFromList.get(inheritFromNum); 145 146 if (attributionTags.contains(inheritFrom)) { 147 // Cannot inherit from a attribution that is still defined 148 return false; 149 } 150 151 boolean wasAdded = inheritFromAttributionTags.add(inheritFrom); 152 if (!wasAdded) { 153 // inheritFrom is not unique 154 return false; 155 } 156 } 157 } 158 159 return true; 160 } 161 } 162