1 /* 2 * Copyright (C) 2007 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 android.content.pm; 18 19 import android.content.ComponentName; 20 import android.content.IntentFilter; 21 import android.graphics.drawable.Drawable; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 import android.os.UserHandle; 25 import android.text.TextUtils; 26 import android.util.Printer; 27 import android.util.Slog; 28 29 import java.text.Collator; 30 import java.util.Comparator; 31 32 /** 33 * Information that is returned from resolving an intent 34 * against an IntentFilter. This partially corresponds to 35 * information collected from the AndroidManifest.xml's 36 * <intent> tags. 37 */ 38 public class ResolveInfo implements Parcelable { 39 private static final String TAG = "ResolveInfo"; 40 41 /** 42 * The activity or broadcast receiver that corresponds to this resolution 43 * match, if this resolution is for an activity or broadcast receiver. 44 * Exactly one of {@link #activityInfo}, {@link #serviceInfo}, or 45 * {@link #providerInfo} will be non-null. 46 */ 47 public ActivityInfo activityInfo; 48 49 /** 50 * The service that corresponds to this resolution match, if this resolution 51 * is for a service. Exactly one of {@link #activityInfo}, 52 * {@link #serviceInfo}, or {@link #providerInfo} will be non-null. 53 */ 54 public ServiceInfo serviceInfo; 55 56 /** 57 * The provider that corresponds to this resolution match, if this 58 * resolution is for a provider. Exactly one of {@link #activityInfo}, 59 * {@link #serviceInfo}, or {@link #providerInfo} will be non-null. 60 */ 61 public ProviderInfo providerInfo; 62 63 /** 64 * The ephemeral application that corresponds to this resolution match. This will 65 * only be set in specific circumstances. 66 * @hide 67 */ 68 public EphemeralResolveInfo ephemeralResolveInfo; 69 70 /** 71 * A ResolveInfo that points at the ephemeral installer. 72 * @hide 73 */ 74 public ResolveInfo ephemeralInstaller; 75 76 /** 77 * The IntentFilter that was matched for this ResolveInfo. 78 */ 79 public IntentFilter filter; 80 81 /** 82 * The declared priority of this match. Comes from the "priority" 83 * attribute or, if not set, defaults to 0. Higher values are a higher 84 * priority. 85 */ 86 public int priority; 87 88 /** 89 * Order of result according to the user's preference. If the user 90 * has not set a preference for this result, the value is 0; higher 91 * values are a higher priority. 92 */ 93 public int preferredOrder; 94 95 /** 96 * The system's evaluation of how well the activity matches the 97 * IntentFilter. This is a match constant, a combination of 98 * {@link IntentFilter#MATCH_CATEGORY_MASK IntentFilter.MATCH_CATEGORY_MASK} 99 * and {@link IntentFilter#MATCH_ADJUSTMENT_MASK IntentFiler.MATCH_ADJUSTMENT_MASK}. 100 */ 101 public int match; 102 103 /** 104 * Only set when returned by 105 * {@link PackageManager#queryIntentActivityOptions}, this tells you 106 * which of the given specific intents this result came from. 0 is the 107 * first in the list, < 0 means it came from the generic Intent query. 108 */ 109 public int specificIndex = -1; 110 111 /** 112 * This filter has specified the Intent.CATEGORY_DEFAULT, meaning it 113 * would like to be considered a default action that the user can 114 * perform on this data. 115 */ 116 public boolean isDefault; 117 118 /** 119 * A string resource identifier (in the package's resources) of this 120 * match's label. From the "label" attribute or, if not set, 0. 121 */ 122 public int labelRes; 123 124 /** 125 * The actual string retrieve from <var>labelRes</var> or null if none 126 * was provided. 127 */ 128 public CharSequence nonLocalizedLabel; 129 130 /** 131 * A drawable resource identifier (in the package's resources) of this 132 * match's icon. From the "icon" attribute or, if not set, 0. It is 133 * set only if the icon can be obtained by resource id alone. 134 */ 135 public int icon; 136 137 /** 138 * Optional -- if non-null, the {@link #labelRes} and {@link #icon} 139 * resources will be loaded from this package, rather than the one 140 * containing the resolved component. 141 */ 142 public String resolvePackageName; 143 144 /** 145 * If not equal to UserHandle.USER_CURRENT, then the intent will be forwarded to this user. 146 * @hide 147 */ 148 public int targetUserId; 149 150 /** 151 * Set to true if the icon cannot be obtained by resource ids alone. 152 * It is set to true for ResolveInfos from the managed profile: They need to 153 * have their icon badged, so it cannot be obtained by resource ids alone. 154 * @hide 155 */ 156 public boolean noResourceId; 157 158 /** 159 * Same as {@link #icon} but it will always correspond to "icon" attribute 160 * regardless of {@link #noResourceId} value. 161 * @hide 162 */ 163 public int iconResourceId; 164 165 /** 166 * @hide Target comes from system process? 167 */ 168 public boolean system; 169 170 /** 171 * @hide Does the associated IntentFilter comes from a Browser ? 172 */ 173 public boolean handleAllWebDataURI; 174 175 /** {@hide} */ getComponentInfo()176 public ComponentInfo getComponentInfo() { 177 if (activityInfo != null) return activityInfo; 178 if (serviceInfo != null) return serviceInfo; 179 if (providerInfo != null) return providerInfo; 180 throw new IllegalStateException("Missing ComponentInfo!"); 181 } 182 183 /** 184 * Retrieve the current textual label associated with this resolution. This 185 * will call back on the given PackageManager to load the label from 186 * the application. 187 * 188 * @param pm A PackageManager from which the label can be loaded; usually 189 * the PackageManager from which you originally retrieved this item. 190 * 191 * @return Returns a CharSequence containing the resolutions's label. If the 192 * item does not have a label, its name is returned. 193 */ loadLabel(PackageManager pm)194 public CharSequence loadLabel(PackageManager pm) { 195 if (nonLocalizedLabel != null) { 196 return nonLocalizedLabel; 197 } 198 CharSequence label; 199 if (resolvePackageName != null && labelRes != 0) { 200 label = pm.getText(resolvePackageName, labelRes, null); 201 if (label != null) { 202 return label.toString().trim(); 203 } 204 } 205 ComponentInfo ci = getComponentInfo(); 206 ApplicationInfo ai = ci.applicationInfo; 207 if (labelRes != 0) { 208 label = pm.getText(ci.packageName, labelRes, ai); 209 if (label != null) { 210 return label.toString().trim(); 211 } 212 } 213 214 CharSequence data = ci.loadLabel(pm); 215 // Make the data safe 216 if (data != null) data = data.toString().trim(); 217 return data; 218 } 219 220 /** 221 * Retrieve the current graphical icon associated with this resolution. This 222 * will call back on the given PackageManager to load the icon from 223 * the application. 224 * 225 * @param pm A PackageManager from which the icon can be loaded; usually 226 * the PackageManager from which you originally retrieved this item. 227 * 228 * @return Returns a Drawable containing the resolution's icon. If the 229 * item does not have an icon, the default activity icon is returned. 230 */ loadIcon(PackageManager pm)231 public Drawable loadIcon(PackageManager pm) { 232 Drawable dr = null; 233 if (resolvePackageName != null && iconResourceId != 0) { 234 dr = pm.getDrawable(resolvePackageName, iconResourceId, null); 235 } 236 ComponentInfo ci = getComponentInfo(); 237 if (dr == null && iconResourceId != 0) { 238 ApplicationInfo ai = ci.applicationInfo; 239 dr = pm.getDrawable(ci.packageName, iconResourceId, ai); 240 } 241 if (dr != null) { 242 return pm.getUserBadgedIcon(dr, new UserHandle(UserHandle.myUserId())); 243 } 244 return ci.loadIcon(pm); 245 } 246 247 /** 248 * Return the icon resource identifier to use for this match. If the 249 * match defines an icon, that is used; else if the activity defines 250 * an icon, that is used; else, the application icon is used. 251 * This function does not check noResourceId flag. 252 * 253 * @return The icon associated with this match. 254 */ getIconResourceInternal()255 final int getIconResourceInternal() { 256 if (iconResourceId != 0) return iconResourceId; 257 final ComponentInfo ci = getComponentInfo(); 258 if (ci != null) { 259 return ci.getIconResource(); 260 } 261 return 0; 262 } 263 264 /** 265 * Return the icon resource identifier to use for this match. If the 266 * match defines an icon, that is used; else if the activity defines 267 * an icon, that is used; else, the application icon is used. 268 * 269 * @return The icon associated with this match. 270 */ getIconResource()271 public final int getIconResource() { 272 if (noResourceId) return 0; 273 return getIconResourceInternal(); 274 } 275 dump(Printer pw, String prefix)276 public void dump(Printer pw, String prefix) { 277 dump(pw, prefix, PackageItemInfo.DUMP_FLAG_ALL); 278 } 279 280 /** @hide */ dump(Printer pw, String prefix, int flags)281 public void dump(Printer pw, String prefix, int flags) { 282 if (filter != null) { 283 pw.println(prefix + "Filter:"); 284 filter.dump(pw, prefix + " "); 285 } 286 pw.println(prefix + "priority=" + priority 287 + " preferredOrder=" + preferredOrder 288 + " match=0x" + Integer.toHexString(match) 289 + " specificIndex=" + specificIndex 290 + " isDefault=" + isDefault); 291 if (resolvePackageName != null) { 292 pw.println(prefix + "resolvePackageName=" + resolvePackageName); 293 } 294 if (labelRes != 0 || nonLocalizedLabel != null || icon != 0) { 295 pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes) 296 + " nonLocalizedLabel=" + nonLocalizedLabel 297 + " icon=0x" + Integer.toHexString(icon)); 298 } 299 if (activityInfo != null) { 300 pw.println(prefix + "ActivityInfo:"); 301 activityInfo.dump(pw, prefix + " ", flags); 302 } else if (serviceInfo != null) { 303 pw.println(prefix + "ServiceInfo:"); 304 serviceInfo.dump(pw, prefix + " ", flags); 305 } else if (providerInfo != null) { 306 pw.println(prefix + "ProviderInfo:"); 307 providerInfo.dump(pw, prefix + " ", flags); 308 } 309 } 310 ResolveInfo()311 public ResolveInfo() { 312 targetUserId = UserHandle.USER_CURRENT; 313 } 314 ResolveInfo(ResolveInfo orig)315 public ResolveInfo(ResolveInfo orig) { 316 activityInfo = orig.activityInfo; 317 serviceInfo = orig.serviceInfo; 318 providerInfo = orig.providerInfo; 319 filter = orig.filter; 320 priority = orig.priority; 321 preferredOrder = orig.preferredOrder; 322 match = orig.match; 323 specificIndex = orig.specificIndex; 324 labelRes = orig.labelRes; 325 nonLocalizedLabel = orig.nonLocalizedLabel; 326 icon = orig.icon; 327 resolvePackageName = orig.resolvePackageName; 328 noResourceId = orig.noResourceId; 329 iconResourceId = orig.iconResourceId; 330 system = orig.system; 331 targetUserId = orig.targetUserId; 332 handleAllWebDataURI = orig.handleAllWebDataURI; 333 } 334 toString()335 public String toString() { 336 final ComponentInfo ci = getComponentInfo(); 337 StringBuilder sb = new StringBuilder(128); 338 sb.append("ResolveInfo{"); 339 sb.append(Integer.toHexString(System.identityHashCode(this))); 340 sb.append(' '); 341 ComponentName.appendShortString(sb, ci.packageName, ci.name); 342 if (priority != 0) { 343 sb.append(" p="); 344 sb.append(priority); 345 } 346 if (preferredOrder != 0) { 347 sb.append(" o="); 348 sb.append(preferredOrder); 349 } 350 sb.append(" m=0x"); 351 sb.append(Integer.toHexString(match)); 352 if (targetUserId != UserHandle.USER_CURRENT) { 353 sb.append(" targetUserId="); 354 sb.append(targetUserId); 355 } 356 sb.append('}'); 357 return sb.toString(); 358 } 359 describeContents()360 public int describeContents() { 361 return 0; 362 } 363 writeToParcel(Parcel dest, int parcelableFlags)364 public void writeToParcel(Parcel dest, int parcelableFlags) { 365 if (activityInfo != null) { 366 dest.writeInt(1); 367 activityInfo.writeToParcel(dest, parcelableFlags); 368 } else if (serviceInfo != null) { 369 dest.writeInt(2); 370 serviceInfo.writeToParcel(dest, parcelableFlags); 371 } else if (providerInfo != null) { 372 dest.writeInt(3); 373 providerInfo.writeToParcel(dest, parcelableFlags); 374 } else { 375 dest.writeInt(0); 376 } 377 if (filter != null) { 378 dest.writeInt(1); 379 filter.writeToParcel(dest, parcelableFlags); 380 } else { 381 dest.writeInt(0); 382 } 383 dest.writeInt(priority); 384 dest.writeInt(preferredOrder); 385 dest.writeInt(match); 386 dest.writeInt(specificIndex); 387 dest.writeInt(labelRes); 388 TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags); 389 dest.writeInt(icon); 390 dest.writeString(resolvePackageName); 391 dest.writeInt(targetUserId); 392 dest.writeInt(system ? 1 : 0); 393 dest.writeInt(noResourceId ? 1 : 0); 394 dest.writeInt(iconResourceId); 395 dest.writeInt(handleAllWebDataURI ? 1 : 0); 396 } 397 398 public static final Creator<ResolveInfo> CREATOR 399 = new Creator<ResolveInfo>() { 400 public ResolveInfo createFromParcel(Parcel source) { 401 return new ResolveInfo(source); 402 } 403 public ResolveInfo[] newArray(int size) { 404 return new ResolveInfo[size]; 405 } 406 }; 407 ResolveInfo(Parcel source)408 private ResolveInfo(Parcel source) { 409 activityInfo = null; 410 serviceInfo = null; 411 providerInfo = null; 412 switch (source.readInt()) { 413 case 1: 414 activityInfo = ActivityInfo.CREATOR.createFromParcel(source); 415 break; 416 case 2: 417 serviceInfo = ServiceInfo.CREATOR.createFromParcel(source); 418 break; 419 case 3: 420 providerInfo = ProviderInfo.CREATOR.createFromParcel(source); 421 break; 422 default: 423 Slog.w(TAG, "Missing ComponentInfo!"); 424 break; 425 } 426 if (source.readInt() != 0) { 427 filter = IntentFilter.CREATOR.createFromParcel(source); 428 } 429 priority = source.readInt(); 430 preferredOrder = source.readInt(); 431 match = source.readInt(); 432 specificIndex = source.readInt(); 433 labelRes = source.readInt(); 434 nonLocalizedLabel 435 = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); 436 icon = source.readInt(); 437 resolvePackageName = source.readString(); 438 targetUserId = source.readInt(); 439 system = source.readInt() != 0; 440 noResourceId = source.readInt() != 0; 441 iconResourceId = source.readInt(); 442 handleAllWebDataURI = source.readInt() != 0; 443 } 444 445 public static class DisplayNameComparator 446 implements Comparator<ResolveInfo> { DisplayNameComparator(PackageManager pm)447 public DisplayNameComparator(PackageManager pm) { 448 mPM = pm; 449 mCollator.setStrength(Collator.PRIMARY); 450 } 451 compare(ResolveInfo a, ResolveInfo b)452 public final int compare(ResolveInfo a, ResolveInfo b) { 453 // We want to put the one targeted to another user at the end of the dialog. 454 if (a.targetUserId != UserHandle.USER_CURRENT) { 455 return 1; 456 } 457 if (b.targetUserId != UserHandle.USER_CURRENT) { 458 return -1; 459 } 460 CharSequence sa = a.loadLabel(mPM); 461 if (sa == null) sa = a.activityInfo.name; 462 CharSequence sb = b.loadLabel(mPM); 463 if (sb == null) sb = b.activityInfo.name; 464 465 return mCollator.compare(sa.toString(), sb.toString()); 466 } 467 468 private final Collator mCollator = Collator.getInstance(); 469 private PackageManager mPM; 470 } 471 } 472