PreferredComponent.java revision 4ff5defa5328411035183b785a77db41778bc272
1/* 2 * Copyright (C) 2011 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 com.android.server.pm; 18 19import com.android.internal.util.XmlUtils; 20 21import org.xmlpull.v1.XmlPullParser; 22import org.xmlpull.v1.XmlPullParserException; 23import org.xmlpull.v1.XmlSerializer; 24 25import android.content.ComponentName; 26import android.content.IntentFilter; 27import android.content.pm.ActivityInfo; 28import android.content.pm.ResolveInfo; 29import android.util.Slog; 30 31import java.io.IOException; 32import java.io.PrintWriter; 33import java.util.ArrayList; 34import java.util.List; 35 36public class PreferredComponent { 37 private static final String TAG_SET = "set"; 38 private static final String ATTR_ALWAYS = "always"; // boolean 39 private static final String ATTR_MATCH = "match"; // number 40 private static final String ATTR_NAME = "name"; // component name 41 private static final String ATTR_SET = "set"; // number 42 43 public final int mMatch; 44 public final ComponentName mComponent; 45 // Whether this is to be the one that's always chosen. If false, it's the most recently chosen. 46 public boolean mAlways; 47 48 final String[] mSetPackages; 49 final String[] mSetClasses; 50 final String[] mSetComponents; 51 final String mShortComponent; 52 private String mParseError; 53 54 private final Callbacks mCallbacks; 55 56 public interface Callbacks { 57 public boolean onReadTag(String tagName, XmlPullParser parser) 58 throws XmlPullParserException, IOException; 59 } 60 61 public PreferredComponent(Callbacks callbacks, int match, ComponentName[] set, 62 ComponentName component, boolean always) { 63 mCallbacks = callbacks; 64 mMatch = match&IntentFilter.MATCH_CATEGORY_MASK; 65 mComponent = component; 66 mAlways = always; 67 mShortComponent = component.flattenToShortString(); 68 mParseError = null; 69 if (set != null) { 70 final int N = set.length; 71 String[] myPackages = new String[N]; 72 String[] myClasses = new String[N]; 73 String[] myComponents = new String[N]; 74 for (int i=0; i<N; i++) { 75 ComponentName cn = set[i]; 76 if (cn == null) { 77 mSetPackages = null; 78 mSetClasses = null; 79 mSetComponents = null; 80 return; 81 } 82 myPackages[i] = cn.getPackageName().intern(); 83 myClasses[i] = cn.getClassName().intern(); 84 myComponents[i] = cn.flattenToShortString(); 85 } 86 mSetPackages = myPackages; 87 mSetClasses = myClasses; 88 mSetComponents = myComponents; 89 } else { 90 mSetPackages = null; 91 mSetClasses = null; 92 mSetComponents = null; 93 } 94 } 95 96 public PreferredComponent(Callbacks callbacks, XmlPullParser parser) 97 throws XmlPullParserException, IOException { 98 mCallbacks = callbacks; 99 mShortComponent = parser.getAttributeValue(null, ATTR_NAME); 100 mComponent = ComponentName.unflattenFromString(mShortComponent); 101 if (mComponent == null) { 102 mParseError = "Bad activity name " + mShortComponent; 103 } 104 String matchStr = parser.getAttributeValue(null, ATTR_MATCH); 105 mMatch = matchStr != null ? Integer.parseInt(matchStr, 16) : 0; 106 String setCountStr = parser.getAttributeValue(null, ATTR_SET); 107 int setCount = setCountStr != null ? Integer.parseInt(setCountStr) : 0; 108 String alwaysStr = parser.getAttributeValue(null, ATTR_ALWAYS); 109 mAlways = alwaysStr != null ? Boolean.parseBoolean(alwaysStr) : true; 110 111 String[] myPackages = setCount > 0 ? new String[setCount] : null; 112 String[] myClasses = setCount > 0 ? new String[setCount] : null; 113 String[] myComponents = setCount > 0 ? new String[setCount] : null; 114 115 int setPos = 0; 116 117 int outerDepth = parser.getDepth(); 118 int type; 119 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 120 && (type != XmlPullParser.END_TAG 121 || parser.getDepth() > outerDepth)) { 122 if (type == XmlPullParser.END_TAG 123 || type == XmlPullParser.TEXT) { 124 continue; 125 } 126 127 String tagName = parser.getName(); 128 //Log.i(TAG, "Parse outerDepth=" + outerDepth + " depth=" 129 // + parser.getDepth() + " tag=" + tagName); 130 if (tagName.equals(TAG_SET)) { 131 String name = parser.getAttributeValue(null, ATTR_NAME); 132 if (name == null) { 133 if (mParseError == null) { 134 mParseError = "No name in set tag in preferred activity " 135 + mShortComponent; 136 } 137 } else if (setPos >= setCount) { 138 if (mParseError == null) { 139 mParseError = "Too many set tags in preferred activity " 140 + mShortComponent; 141 } 142 } else { 143 ComponentName cn = ComponentName.unflattenFromString(name); 144 if (cn == null) { 145 if (mParseError == null) { 146 mParseError = "Bad set name " + name + " in preferred activity " 147 + mShortComponent; 148 } 149 } else { 150 myPackages[setPos] = cn.getPackageName(); 151 myClasses[setPos] = cn.getClassName(); 152 myComponents[setPos] = name; 153 setPos++; 154 } 155 } 156 XmlUtils.skipCurrentTag(parser); 157 } else if (!mCallbacks.onReadTag(tagName, parser)) { 158 Slog.w("PreferredComponent", "Unknown element: " + parser.getName()); 159 XmlUtils.skipCurrentTag(parser); 160 } 161 } 162 163 if (setPos != setCount) { 164 if (mParseError == null) { 165 mParseError = "Not enough set tags (expected " + setCount 166 + " but found " + setPos + ") in " + mShortComponent; 167 } 168 } 169 170 mSetPackages = myPackages; 171 mSetClasses = myClasses; 172 mSetComponents = myComponents; 173 } 174 175 public String getParseError() { 176 return mParseError; 177 } 178 179 public void writeToXml(XmlSerializer serializer, boolean full) throws IOException { 180 final int NS = mSetClasses != null ? mSetClasses.length : 0; 181 serializer.attribute(null, ATTR_NAME, mShortComponent); 182 if (full) { 183 if (mMatch != 0) { 184 serializer.attribute(null, ATTR_MATCH, Integer.toHexString(mMatch)); 185 } 186 serializer.attribute(null, ATTR_ALWAYS, Boolean.toString(mAlways)); 187 serializer.attribute(null, ATTR_SET, Integer.toString(NS)); 188 for (int s=0; s<NS; s++) { 189 serializer.startTag(null, TAG_SET); 190 serializer.attribute(null, ATTR_NAME, mSetComponents[s]); 191 serializer.endTag(null, TAG_SET); 192 } 193 } 194 } 195 196 public boolean sameSet(List<ResolveInfo> query) { 197 if (mSetPackages == null) { 198 return query == null; 199 } 200 if (query == null) { 201 return false; 202 } 203 final int NQ = query.size(); 204 final int NS = mSetPackages.length; 205 206 int numMatch = 0; 207 for (int i=0; i<NQ; i++) { 208 ResolveInfo ri = query.get(i); 209 ActivityInfo ai = ri.activityInfo; 210 boolean good = false; 211 for (int j=0; j<NS; j++) { 212 if (mSetPackages[j].equals(ai.packageName) 213 && mSetClasses[j].equals(ai.name)) { 214 numMatch++; 215 good = true; 216 break; 217 } 218 } 219 if (!good) return false; 220 } 221 return numMatch == NS; 222 } 223 224 public boolean sameSet(ComponentName[] comps) { 225 if (mSetPackages == null) return false; 226 final int NQ = comps.length; 227 final int NS = mSetPackages.length; 228 int numMatch = 0; 229 for (int i=0; i<NQ; i++) { 230 ComponentName cn = comps[i]; 231 boolean good = false; 232 for (int j=0; j<NS; j++) { 233 if (mSetPackages[j].equals(cn.getPackageName()) 234 && mSetClasses[j].equals(cn.getClassName())) { 235 numMatch++; 236 good = true; 237 break; 238 } 239 } 240 if (!good) return false; 241 } 242 return numMatch == NS; 243 } 244 245 public boolean isSuperset(List<ResolveInfo> query) { 246 if (mSetPackages == null) { 247 return query == null; 248 } 249 if (query == null) { 250 return true; 251 } 252 final int NQ = query.size(); 253 final int NS = mSetPackages.length; 254 if (NS < NQ) { 255 return false; 256 } 257 for (int i=0; i<NQ; i++) { 258 ResolveInfo ri = query.get(i); 259 ActivityInfo ai = ri.activityInfo; 260 boolean foundMatch = false; 261 for (int j=0; j<NS; j++) { 262 if (mSetPackages[j].equals(ai.packageName) && mSetClasses[j].equals(ai.name)) { 263 foundMatch = true; 264 break; 265 } 266 } 267 if (!foundMatch) return false; 268 } 269 return true; 270 } 271 272 /** Returns components from mSetPackages that are present in query. */ 273 public ComponentName[] discardObsoleteComponents(List<ResolveInfo> query) { 274 if (mSetPackages == null || query == null) { 275 return new ComponentName[0]; 276 } 277 final int NQ = query.size(); 278 final int NS = mSetPackages.length; 279 ArrayList<ComponentName> aliveComponents = new ArrayList<>(); 280 for (int i = 0; i < NQ; i++) { 281 ResolveInfo ri = query.get(i); 282 ActivityInfo ai = ri.activityInfo; 283 for (int j = 0; j < NS; j++) { 284 if (mSetPackages[j].equals(ai.packageName) && mSetClasses[j].equals(ai.name)) { 285 aliveComponents.add(new ComponentName(mSetPackages[j], mSetClasses[j])); 286 break; 287 } 288 } 289 } 290 return aliveComponents.toArray(new ComponentName[aliveComponents.size()]); 291 } 292 293 public void dump(PrintWriter out, String prefix, Object ident) { 294 out.print(prefix); out.print( 295 Integer.toHexString(System.identityHashCode(ident))); 296 out.print(' '); 297 out.println(mShortComponent); 298 out.print(prefix); out.print(" mMatch=0x"); 299 out.print(Integer.toHexString(mMatch)); 300 out.print(" mAlways="); out.println(mAlways); 301 if (mSetComponents != null) { 302 out.print(prefix); out.println(" Selected from:"); 303 for (int i=0; i<mSetComponents.length; i++) { 304 out.print(prefix); out.print(" "); 305 out.println(mSetComponents[i]); 306 } 307 } 308 } 309} 310