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