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