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