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