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 17 package com.android.server.pm; 18 19 import android.content.ComponentName; 20 import android.content.IntentFilter; 21 import android.content.pm.ActivityInfo; 22 import android.content.pm.PackageManager; 23 import android.content.pm.PackageManagerInternal; 24 import android.content.pm.ResolveInfo; 25 import android.util.Slog; 26 27 import com.android.internal.util.XmlUtils; 28 import com.android.modules.utils.TypedXmlPullParser; 29 import com.android.modules.utils.TypedXmlSerializer; 30 import com.android.server.LocalServices; 31 import com.android.server.pm.pkg.PackageUserState; 32 33 import org.xmlpull.v1.XmlPullParser; 34 import org.xmlpull.v1.XmlPullParserException; 35 36 import java.io.IOException; 37 import java.io.PrintWriter; 38 import java.util.ArrayList; 39 import java.util.List; 40 41 public class PreferredComponent { 42 private static final String TAG_SET = "set"; 43 private static final String ATTR_ALWAYS = "always"; // boolean 44 private static final String ATTR_MATCH = "match"; // number 45 private static final String ATTR_NAME = "name"; // component name 46 private static final String ATTR_SET = "set"; // number 47 48 public final int mMatch; 49 public final ComponentName mComponent; 50 // Whether this is to be the one that's always chosen. If false, it's the most recently chosen. 51 public boolean mAlways; 52 53 final String[] mSetPackages; 54 final String[] mSetClasses; 55 final String[] mSetComponents; 56 final String mShortComponent; 57 private String mParseError; 58 59 private final Callbacks mCallbacks; 60 61 public interface Callbacks { onReadTag(String tagName, TypedXmlPullParser parser)62 public boolean onReadTag(String tagName, TypedXmlPullParser parser) 63 throws XmlPullParserException, IOException; 64 } 65 PreferredComponent(Callbacks callbacks, int match, ComponentName[] set, ComponentName component, boolean always)66 public PreferredComponent(Callbacks callbacks, int match, ComponentName[] set, 67 ComponentName component, boolean always) { 68 mCallbacks = callbacks; 69 mMatch = match&IntentFilter.MATCH_CATEGORY_MASK; 70 mComponent = component; 71 mAlways = always; 72 mShortComponent = component.flattenToShortString(); 73 mParseError = null; 74 if (set != null) { 75 final int N = set.length; 76 String[] myPackages = new String[N]; 77 String[] myClasses = new String[N]; 78 String[] myComponents = new String[N]; 79 for (int i=0; i<N; i++) { 80 ComponentName cn = set[i]; 81 if (cn == null) { 82 mSetPackages = null; 83 mSetClasses = null; 84 mSetComponents = null; 85 return; 86 } 87 myPackages[i] = cn.getPackageName().intern(); 88 myClasses[i] = cn.getClassName().intern(); 89 myComponents[i] = cn.flattenToShortString(); 90 } 91 mSetPackages = myPackages; 92 mSetClasses = myClasses; 93 mSetComponents = myComponents; 94 } else { 95 mSetPackages = null; 96 mSetClasses = null; 97 mSetComponents = null; 98 } 99 } 100 PreferredComponent(Callbacks callbacks, TypedXmlPullParser parser)101 public PreferredComponent(Callbacks callbacks, TypedXmlPullParser parser) 102 throws XmlPullParserException, IOException { 103 mCallbacks = callbacks; 104 mShortComponent = parser.getAttributeValue(null, ATTR_NAME); 105 mComponent = ComponentName.unflattenFromString(mShortComponent); 106 if (mComponent == null) { 107 mParseError = "Bad activity name " + mShortComponent; 108 } 109 mMatch = parser.getAttributeIntHex(null, ATTR_MATCH, 0); 110 int setCount = parser.getAttributeInt(null, ATTR_SET, 0); 111 mAlways = parser.getAttributeBoolean(null, ATTR_ALWAYS, true); 112 113 String[] myPackages = setCount > 0 ? new String[setCount] : null; 114 String[] myClasses = setCount > 0 ? new String[setCount] : null; 115 String[] myComponents = setCount > 0 ? new String[setCount] : null; 116 117 int setPos = 0; 118 119 int outerDepth = parser.getDepth(); 120 int type; 121 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 122 && (type != XmlPullParser.END_TAG 123 || parser.getDepth() > outerDepth)) { 124 if (type == XmlPullParser.END_TAG 125 || type == XmlPullParser.TEXT) { 126 continue; 127 } 128 129 String tagName = parser.getName(); 130 //Log.i(TAG, "Parse outerDepth=" + outerDepth + " depth=" 131 // + parser.getDepth() + " tag=" + tagName); 132 if (tagName.equals(TAG_SET)) { 133 String name = parser.getAttributeValue(null, ATTR_NAME); 134 if (name == null) { 135 if (mParseError == null) { 136 mParseError = "No name in set tag in preferred activity " 137 + mShortComponent; 138 } 139 } else if (setPos >= setCount) { 140 if (mParseError == null) { 141 mParseError = "Too many set tags in preferred activity " 142 + mShortComponent; 143 } 144 } else { 145 ComponentName cn = ComponentName.unflattenFromString(name); 146 if (cn == null) { 147 if (mParseError == null) { 148 mParseError = "Bad set name " + name + " in preferred activity " 149 + mShortComponent; 150 } 151 } else { 152 myPackages[setPos] = cn.getPackageName(); 153 myClasses[setPos] = cn.getClassName(); 154 myComponents[setPos] = name; 155 setPos++; 156 } 157 } 158 XmlUtils.skipCurrentTag(parser); 159 } else if (!mCallbacks.onReadTag(tagName, parser)) { 160 Slog.w("PreferredComponent", "Unknown element: " + parser.getName()); 161 XmlUtils.skipCurrentTag(parser); 162 } 163 } 164 165 if (setPos != setCount) { 166 if (mParseError == null) { 167 mParseError = "Not enough set tags (expected " + setCount 168 + " but found " + setPos + ") in " + mShortComponent; 169 } 170 } 171 172 mSetPackages = myPackages; 173 mSetClasses = myClasses; 174 mSetComponents = myComponents; 175 } 176 getParseError()177 public String getParseError() { 178 return mParseError; 179 } 180 writeToXml(TypedXmlSerializer serializer, boolean full)181 public void writeToXml(TypedXmlSerializer serializer, boolean full) throws IOException { 182 final int NS = mSetClasses != null ? mSetClasses.length : 0; 183 serializer.attribute(null, ATTR_NAME, mShortComponent); 184 if (full) { 185 if (mMatch != 0) { 186 serializer.attributeIntHex(null, ATTR_MATCH, mMatch); 187 } 188 serializer.attributeBoolean(null, ATTR_ALWAYS, mAlways); 189 serializer.attributeInt(null, ATTR_SET, NS); 190 for (int s=0; s<NS; s++) { 191 serializer.startTag(null, TAG_SET); 192 serializer.attribute(null, ATTR_NAME, mSetComponents[s]); 193 serializer.endTag(null, TAG_SET); 194 } 195 } 196 } 197 sameSet(List<ResolveInfo> query, boolean excludeSetupWizardPackage, int userId)198 public boolean sameSet(List<ResolveInfo> query, boolean excludeSetupWizardPackage, int userId) { 199 if (mSetPackages == null) { 200 return query == null; 201 } 202 if (query == null) { 203 return false; 204 } 205 final int NQ = query.size(); 206 final int NS = mSetPackages.length; 207 final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); 208 String setupWizardPackageName = pmi.getSetupWizardPackageName(); 209 int numMatch = 0; 210 for (int i=0; i<NQ; i++) { 211 ResolveInfo ri = query.get(i); 212 ActivityInfo ai = ri.activityInfo; 213 boolean good = false; 214 215 // ignore SetupWizard package's launcher capability because it is only existed 216 // during SetupWizard is running 217 if (excludeSetupWizardPackage && ai.packageName.equals(setupWizardPackageName)) { 218 continue; 219 } 220 221 // Avoid showing the disambiguation dialog if the package which is installed with 222 // reason INSTALL_REASON_DEVICE_SETUP. 223 final PackageUserState pkgUserState = 224 pmi.getPackageStateInternal(ai.packageName).getUserStates().get(userId); 225 if (pkgUserState != null && pkgUserState.getInstallReason() 226 == PackageManager.INSTALL_REASON_DEVICE_SETUP) { 227 continue; 228 } 229 230 for (int j=0; j<NS; j++) { 231 if (mSetPackages[j].equals(ai.packageName) 232 && mSetClasses[j].equals(ai.name)) { 233 numMatch++; 234 good = true; 235 break; 236 } 237 } 238 if (!good) return false; 239 } 240 return numMatch == NS; 241 } 242 sameSet(ComponentName[] comps)243 public boolean sameSet(ComponentName[] comps) { 244 if (mSetPackages == null) return false; 245 final int NQ = comps.length; 246 final int NS = mSetPackages.length; 247 int numMatch = 0; 248 for (int i=0; i<NQ; i++) { 249 ComponentName cn = comps[i]; 250 boolean good = false; 251 for (int j=0; j<NS; j++) { 252 if (mSetPackages[j].equals(cn.getPackageName()) 253 && mSetClasses[j].equals(cn.getClassName())) { 254 numMatch++; 255 good = true; 256 break; 257 } 258 } 259 if (!good) return false; 260 } 261 return numMatch == NS; 262 } 263 sameSet(PreferredComponent pc)264 public boolean sameSet(PreferredComponent pc) { 265 if (mSetPackages == null || pc == null || pc.mSetPackages == null 266 || !sameComponent(pc.mComponent)) { 267 return false; 268 } 269 final int otherPackageCount = pc.mSetPackages.length; 270 final int packageCount = mSetPackages.length; 271 if (otherPackageCount != packageCount) { 272 return false; 273 } 274 for (int i = 0; i < packageCount; i++) { 275 if (!mSetPackages[i].equals(pc.mSetPackages[i]) 276 || !mSetClasses[i].equals(pc.mSetClasses[i])) { 277 return false; 278 } 279 } 280 return true; 281 } 282 283 /** Returns true if the preferred component represents the provided ComponentName. */ sameComponent(ComponentName comp)284 private boolean sameComponent(ComponentName comp) { 285 if (mComponent == null || comp == null) { 286 return false; 287 } 288 if (mComponent.getPackageName().equals(comp.getPackageName()) 289 && mComponent.getClassName().equals(comp.getClassName())) { 290 return true; 291 } 292 return false; 293 } 294 isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage)295 public boolean isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage) { 296 if (mSetPackages == null) { 297 return query == null; 298 } 299 if (query == null) { 300 return true; 301 } 302 final int NQ = query.size(); 303 final int NS = mSetPackages.length; 304 if (!excludeSetupWizardPackage && NS < NQ) { 305 return false; 306 } 307 final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); 308 String setupWizardPackageName = pmi.getSetupWizardPackageName(); 309 for (int i=0; i<NQ; i++) { 310 ResolveInfo ri = query.get(i); 311 ActivityInfo ai = ri.activityInfo; 312 boolean foundMatch = false; 313 314 // ignore SetupWizard package's launcher capability because it is only existed 315 // during SetupWizard is running 316 if (excludeSetupWizardPackage && ai.packageName.equals(setupWizardPackageName)) { 317 continue; 318 } 319 320 for (int j=0; j<NS; j++) { 321 if (mSetPackages[j].equals(ai.packageName) && mSetClasses[j].equals(ai.name)) { 322 foundMatch = true; 323 break; 324 } 325 } 326 if (!foundMatch) return false; 327 } 328 return true; 329 } 330 331 /** Returns components from mSetPackages that are present in query. */ discardObsoleteComponents(List<ResolveInfo> query)332 public ComponentName[] discardObsoleteComponents(List<ResolveInfo> query) { 333 if (mSetPackages == null || query == null) { 334 return new ComponentName[0]; 335 } 336 final int NQ = query.size(); 337 final int NS = mSetPackages.length; 338 ArrayList<ComponentName> aliveComponents = new ArrayList<>(); 339 for (int i = 0; i < NQ; i++) { 340 ResolveInfo ri = query.get(i); 341 ActivityInfo ai = ri.activityInfo; 342 for (int j = 0; j < NS; j++) { 343 if (mSetPackages[j].equals(ai.packageName) && mSetClasses[j].equals(ai.name)) { 344 aliveComponents.add(new ComponentName(mSetPackages[j], mSetClasses[j])); 345 break; 346 } 347 } 348 } 349 return aliveComponents.toArray(new ComponentName[aliveComponents.size()]); 350 } 351 dump(PrintWriter out, String prefix, Object ident)352 public void dump(PrintWriter out, String prefix, Object ident) { 353 out.print(prefix); out.print( 354 Integer.toHexString(System.identityHashCode(ident))); 355 out.print(' '); 356 out.println(mShortComponent); 357 out.print(prefix); out.print(" mMatch=0x"); 358 out.print(Integer.toHexString(mMatch)); 359 out.print(" mAlways="); out.println(mAlways); 360 if (mSetComponents != null) { 361 out.print(prefix); out.println(" Selected from:"); 362 for (int i=0; i<mSetComponents.length; i++) { 363 out.print(prefix); out.print(" "); 364 out.println(mSetComponents[i]); 365 } 366 } 367 } 368 } 369