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