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 static android.text.TextUtils.SAFE_STRING_FLAG_FIRST_LINE; 20 import static android.text.TextUtils.SAFE_STRING_FLAG_SINGLE_LINE; 21 import static android.text.TextUtils.SAFE_STRING_FLAG_TRIM; 22 import static android.text.TextUtils.makeSafeForPresentation; 23 24 import android.annotation.FlaggedApi; 25 import android.annotation.FloatRange; 26 import android.annotation.NonNull; 27 import android.annotation.SystemApi; 28 import android.app.ActivityThread; 29 import android.content.res.XmlResourceParser; 30 import android.graphics.drawable.Drawable; 31 import android.os.Bundle; 32 import android.os.Parcel; 33 import android.os.UserHandle; 34 import android.text.TextUtils; 35 import android.util.Printer; 36 import android.util.proto.ProtoOutputStream; 37 38 39 import java.text.Collator; 40 import java.util.Comparator; 41 import java.util.Objects; 42 43 /** 44 * Base class containing information common to all package items held by 45 * the package manager. This provides a very common basic set of attributes: 46 * a label, icon, and meta-data. This class is not intended 47 * to be used by itself; it is simply here to share common definitions 48 * between all items returned by the package manager. As such, it does not 49 * itself implement Parcelable, but does provide convenience methods to assist 50 * in the implementation of Parcelable in subclasses. 51 */ 52 @android.ravenwood.annotation.RavenwoodKeepWholeClass 53 public class PackageItemInfo { 54 55 /** 56 * The maximum length of a safe label, in characters 57 * 58 * TODO(b/157997155): It may make sense to expose this publicly so that apps can check for the 59 * value and truncate the strings/use a different label, without having to hardcode and make 60 * assumptions about the value. 61 * @hide 62 */ 63 public static final int MAX_SAFE_LABEL_LENGTH = 1000; 64 65 /** @hide */ 66 public static final float DEFAULT_MAX_LABEL_SIZE_PX = 1000f; 67 68 /** 69 * Remove {@link Character#isWhitespace(int) whitespace} and non-breaking spaces from the edges 70 * of the label. 71 * 72 * @see #loadSafeLabel(PackageManager, float, int) 73 * 74 * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_TRIM} instead 75 * @hide 76 * @removed 77 */ 78 @Deprecated 79 @SystemApi 80 public static final int SAFE_LABEL_FLAG_TRIM = SAFE_STRING_FLAG_TRIM; 81 82 /** 83 * Force entire string into single line of text (no newlines). Cannot be set at the same time as 84 * {@link #SAFE_LABEL_FLAG_FIRST_LINE}. 85 * 86 * @see #loadSafeLabel(PackageManager, float, int) 87 * 88 * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_SINGLE_LINE} instead 89 * @hide 90 * @removed 91 */ 92 @Deprecated 93 @SystemApi 94 public static final int SAFE_LABEL_FLAG_SINGLE_LINE = SAFE_STRING_FLAG_SINGLE_LINE; 95 96 /** 97 * Return only first line of text (truncate at first newline). Cannot be set at the same time as 98 * {@link #SAFE_LABEL_FLAG_SINGLE_LINE}. 99 * 100 * @see #loadSafeLabel(PackageManager, float, int) 101 * 102 * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_FIRST_LINE} instead 103 * @hide 104 * @removed 105 */ 106 @Deprecated 107 @SystemApi 108 public static final int SAFE_LABEL_FLAG_FIRST_LINE = SAFE_STRING_FLAG_FIRST_LINE; 109 110 private static volatile boolean sForceSafeLabels = false; 111 112 /** 113 * Always use {@link #loadSafeLabel safe labels} when calling {@link #loadLabel}. 114 * 115 * @hide 116 */ 117 @SystemApi forceSafeLabels()118 public static void forceSafeLabels() { 119 sForceSafeLabels = true; 120 } 121 122 /** 123 * Public name of this item. From the "android:name" attribute. 124 */ 125 public String name; 126 127 /** 128 * Name of the package that this item is in. 129 */ 130 public String packageName; 131 132 /** 133 * A string resource identifier (in the package's resources) of this 134 * component's label. From the "label" attribute or, if not set, 0. 135 */ 136 public int labelRes; 137 138 /** 139 * The string provided in the AndroidManifest file, if any. You 140 * probably don't want to use this. You probably want 141 * {@link PackageManager#getApplicationLabel} 142 */ 143 public CharSequence nonLocalizedLabel; 144 145 /** 146 * A drawable resource identifier (in the package's resources) of this 147 * component's icon. From the "icon" attribute or, if not set, 0. 148 */ 149 public int icon; 150 151 /** 152 * A drawable resource identifier (in the package's resources) of this 153 * component's banner. From the "banner" attribute or, if not set, 0. 154 */ 155 public int banner; 156 157 /** 158 * A drawable resource identifier (in the package's resources) of this 159 * component's logo. Logos may be larger/wider than icons and are 160 * displayed by certain UI elements in place of a name or name/icon 161 * combination. From the "logo" attribute or, if not set, 0. 162 */ 163 public int logo; 164 165 /** 166 * Additional meta-data associated with this component. This field 167 * will only be filled in if you set the 168 * {@link PackageManager#GET_META_DATA} flag when requesting the info. 169 */ 170 public Bundle metaData; 171 172 /** 173 * If different of UserHandle.USER_NULL, The icon of this item will represent that user. 174 * @hide 175 */ 176 public int showUserIcon; 177 178 /** 179 * Whether the package is currently in an archived state. 180 * 181 * <p>Packages can be archived through {@link PackageInstaller#requestArchive} and do not have 182 * any APKs stored on the device, but do keep the data directory. 183 * 184 */ 185 @FlaggedApi(Flags.FLAG_ARCHIVING) 186 public boolean isArchived; 187 PackageItemInfo()188 public PackageItemInfo() { 189 showUserIcon = UserHandle.USER_NULL; 190 } 191 PackageItemInfo(PackageItemInfo orig)192 public PackageItemInfo(PackageItemInfo orig) { 193 name = orig.name; 194 if (name != null) name = name.trim(); 195 packageName = orig.packageName; 196 labelRes = orig.labelRes; 197 nonLocalizedLabel = orig.nonLocalizedLabel; 198 if (nonLocalizedLabel != null) nonLocalizedLabel = nonLocalizedLabel.toString().trim(); 199 icon = orig.icon; 200 banner = orig.banner; 201 logo = orig.logo; 202 metaData = orig.metaData; 203 showUserIcon = orig.showUserIcon; 204 isArchived = orig.isArchived; 205 } 206 207 /** 208 * Retrieve the current textual label associated with this item. 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 item's label. If the 216 * item does not have a label, its name is returned. 217 */ 218 @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.content.res.Resources.class) loadLabel(@onNull PackageManager pm)219 public @NonNull CharSequence loadLabel(@NonNull PackageManager pm) { 220 if (sForceSafeLabels && !Objects.equals(packageName, ActivityThread.currentPackageName())) { 221 return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM 222 | SAFE_STRING_FLAG_FIRST_LINE); 223 } else { 224 // Trims the label string to the MAX_SAFE_LABEL_LENGTH. This is to prevent that the 225 // system is overwhelmed by an enormous string returned by the application. 226 return TextUtils.trimToSize(loadUnsafeLabel(pm), MAX_SAFE_LABEL_LENGTH); 227 } 228 } 229 230 /** {@hide} */ 231 @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.content.res.Resources.class) loadUnsafeLabel(PackageManager pm)232 public CharSequence loadUnsafeLabel(PackageManager pm) { 233 if (nonLocalizedLabel != null) { 234 return nonLocalizedLabel; 235 } 236 if (labelRes != 0) { 237 CharSequence label = pm.getText(packageName, labelRes, getApplicationInfo()); 238 if (label != null) { 239 return label.toString().trim(); 240 } 241 } 242 if (name != null) { 243 return name; 244 } 245 return packageName; 246 } 247 248 /** 249 * @hide 250 * @deprecated use loadSafeLabel(PackageManager, float, int) instead 251 */ 252 @SystemApi 253 @Deprecated 254 @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.content.res.Resources.class) loadSafeLabel(@onNull PackageManager pm)255 public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) { 256 return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM 257 | SAFE_STRING_FLAG_FIRST_LINE); 258 } 259 260 /** 261 * Calls {@link TextUtils#makeSafeForPresentation} for the label of this item. 262 * 263 * <p>For parameters see {@link TextUtils#makeSafeForPresentation}. 264 * 265 * @hide 266 */ 267 @SystemApi 268 @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.content.res.Resources.class) loadSafeLabel(@onNull PackageManager pm, @FloatRange(from = 0) float ellipsizeDip, @TextUtils.SafeStringFlags int flags)269 public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm, 270 @FloatRange(from = 0) float ellipsizeDip, @TextUtils.SafeStringFlags int flags) { 271 Objects.requireNonNull(pm); 272 273 return makeSafeForPresentation(loadUnsafeLabel(pm).toString(), MAX_SAFE_LABEL_LENGTH, 274 ellipsizeDip, flags); 275 } 276 277 /** 278 * Retrieve the current graphical icon associated with this item. This 279 * will call back on the given PackageManager to load the icon from 280 * the application. 281 * 282 * @param pm A PackageManager from which the icon can be loaded; usually 283 * the PackageManager from which you originally retrieved this item. 284 * 285 * @return Returns a Drawable containing the item's icon. If the 286 * item does not have an icon, the item's default icon is returned 287 * such as the default activity icon. 288 */ 289 @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.content.res.Resources.class) loadIcon(PackageManager pm)290 public Drawable loadIcon(PackageManager pm) { 291 return pm.loadItemIcon(this, getApplicationInfo()); 292 } 293 294 /** 295 * Retrieve the current graphical icon associated with this item without 296 * the addition of a work badge if applicable. 297 * This will call back on the given PackageManager to load the icon from 298 * the application. 299 * 300 * @param pm A PackageManager from which the icon can be loaded; usually 301 * the PackageManager from which you originally retrieved this item. 302 * 303 * @return Returns a Drawable containing the item's icon. If the 304 * item does not have an icon, the item's default icon is returned 305 * such as the default activity icon. 306 */ 307 @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.content.res.Resources.class) loadUnbadgedIcon(PackageManager pm)308 public Drawable loadUnbadgedIcon(PackageManager pm) { 309 return pm.loadUnbadgedItemIcon(this, getApplicationInfo()); 310 } 311 312 /** 313 * Retrieve the current graphical banner associated with this item. This 314 * will call back on the given PackageManager to load the banner from 315 * the application. 316 * 317 * @param pm A PackageManager from which the banner can be loaded; usually 318 * the PackageManager from which you originally retrieved this item. 319 * 320 * @return Returns a Drawable containing the item's banner. If the item 321 * does not have a banner, this method will return null. 322 */ 323 @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.content.res.Resources.class) loadBanner(PackageManager pm)324 public Drawable loadBanner(PackageManager pm) { 325 if (banner != 0) { 326 Drawable dr = pm.getDrawable(packageName, banner, getApplicationInfo()); 327 if (dr != null) { 328 return dr; 329 } 330 } 331 return loadDefaultBanner(pm); 332 } 333 334 /** 335 * Retrieve the default graphical icon associated with this item. 336 * 337 * @param pm A PackageManager from which the icon can be loaded; usually 338 * the PackageManager from which you originally retrieved this item. 339 * 340 * @return Returns a Drawable containing the item's default icon 341 * such as the default activity icon. 342 * 343 * @hide 344 */ 345 @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.content.res.Resources.class) loadDefaultIcon(PackageManager pm)346 public Drawable loadDefaultIcon(PackageManager pm) { 347 return pm.getDefaultActivityIcon(); 348 } 349 350 /** 351 * Retrieve the default graphical banner associated with this item. 352 * 353 * @param pm A PackageManager from which the banner can be loaded; usually 354 * the PackageManager from which you originally retrieved this item. 355 * 356 * @return Returns a Drawable containing the item's default banner 357 * or null if no default logo is available. 358 * 359 * @hide 360 */ 361 @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.content.res.Resources.class) loadDefaultBanner(PackageManager pm)362 protected Drawable loadDefaultBanner(PackageManager pm) { 363 return null; 364 } 365 366 /** 367 * Retrieve the current graphical logo associated with this item. This 368 * will call back on the given PackageManager to load the logo from 369 * the application. 370 * 371 * @param pm A PackageManager from which the logo can be loaded; usually 372 * the PackageManager from which you originally retrieved this item. 373 * 374 * @return Returns a Drawable containing the item's logo. If the item 375 * does not have a logo, this method will return null. 376 */ 377 @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.content.res.Resources.class) loadLogo(PackageManager pm)378 public Drawable loadLogo(PackageManager pm) { 379 if (logo != 0) { 380 Drawable d = pm.getDrawable(packageName, logo, getApplicationInfo()); 381 if (d != null) { 382 return d; 383 } 384 } 385 return loadDefaultLogo(pm); 386 } 387 388 /** 389 * Retrieve the default graphical logo associated with this item. 390 * 391 * @param pm A PackageManager from which the logo can be loaded; usually 392 * the PackageManager from which you originally retrieved this item. 393 * 394 * @return Returns a Drawable containing the item's default logo 395 * or null if no default logo is available. 396 * 397 * @hide 398 */ 399 @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.content.res.Resources.class) loadDefaultLogo(PackageManager pm)400 protected Drawable loadDefaultLogo(PackageManager pm) { 401 return null; 402 } 403 404 /** 405 * Load an XML resource attached to the meta-data of this item. This will 406 * retrieved the name meta-data entry, and if defined call back on the 407 * given PackageManager to load its XML file from the application. 408 * 409 * @param pm A PackageManager from which the XML can be loaded; usually 410 * the PackageManager from which you originally retrieved this item. 411 * @param name Name of the meta-date you would like to load. 412 * 413 * @return Returns an XmlPullParser you can use to parse the XML file 414 * assigned as the given meta-data. If the meta-data name is not defined 415 * or the XML resource could not be found, null is returned. 416 */ 417 @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.content.res.Resources.class) loadXmlMetaData(PackageManager pm, String name)418 public XmlResourceParser loadXmlMetaData(PackageManager pm, String name) { 419 if (metaData != null) { 420 int resid = metaData.getInt(name); 421 if (resid != 0) { 422 return pm.getXml(packageName, resid, getApplicationInfo()); 423 } 424 } 425 return null; 426 } 427 428 /** 429 * @hide Flag for dumping: include all details. 430 */ 431 public static final int DUMP_FLAG_DETAILS = 1<<0; 432 433 /** 434 * @hide Flag for dumping: include nested ApplicationInfo. 435 */ 436 public static final int DUMP_FLAG_APPLICATION = 1<<1; 437 438 /** 439 * @hide Flag for dumping: all flags to dump everything. 440 */ 441 public static final int DUMP_FLAG_ALL = DUMP_FLAG_DETAILS | DUMP_FLAG_APPLICATION; 442 dumpFront(Printer pw, String prefix)443 protected void dumpFront(Printer pw, String prefix) { 444 if (name != null) { 445 pw.println(prefix + "name=" + name); 446 } 447 pw.println(prefix + "packageName=" + packageName); 448 if (labelRes != 0 || nonLocalizedLabel != null || icon != 0 || banner != 0) { 449 pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes) 450 + " nonLocalizedLabel=" + nonLocalizedLabel 451 + " icon=0x" + Integer.toHexString(icon) 452 + " banner=0x" + Integer.toHexString(banner)); 453 } 454 } 455 dumpBack(Printer pw, String prefix)456 protected void dumpBack(Printer pw, String prefix) { 457 // no back here 458 } 459 writeToParcel(Parcel dest, int parcelableFlags)460 public void writeToParcel(Parcel dest, int parcelableFlags) { 461 dest.writeString8(name); 462 dest.writeString8(packageName); 463 dest.writeInt(labelRes); 464 TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags); 465 dest.writeInt(icon); 466 dest.writeInt(logo); 467 dest.writeBundle(metaData); 468 dest.writeInt(banner); 469 dest.writeInt(showUserIcon); 470 dest.writeBoolean(isArchived); 471 } 472 473 /** 474 * @hide 475 */ dumpDebug(ProtoOutputStream proto, long fieldId, int dumpFlags)476 public void dumpDebug(ProtoOutputStream proto, long fieldId, int dumpFlags) { 477 long token = proto.start(fieldId); 478 if (name != null) { 479 proto.write(PackageItemInfoProto.NAME, name); 480 } 481 proto.write(PackageItemInfoProto.PACKAGE_NAME, packageName); 482 proto.write(PackageItemInfoProto.LABEL_RES, labelRes); 483 if (nonLocalizedLabel != null) { 484 proto.write(PackageItemInfoProto.NON_LOCALIZED_LABEL, nonLocalizedLabel.toString()); 485 } 486 proto.write(PackageItemInfoProto.ICON, icon); 487 proto.write(PackageItemInfoProto.BANNER, banner); 488 proto.write(PackageItemInfoProto.IS_ARCHIVED, isArchived); 489 proto.end(token); 490 } 491 PackageItemInfo(Parcel source)492 protected PackageItemInfo(Parcel source) { 493 name = source.readString8(); 494 packageName = source.readString8(); 495 labelRes = source.readInt(); 496 nonLocalizedLabel 497 = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); 498 icon = source.readInt(); 499 logo = source.readInt(); 500 metaData = source.readBundle(); 501 banner = source.readInt(); 502 showUserIcon = source.readInt(); 503 isArchived = source.readBoolean(); 504 } 505 506 /** 507 * Get the ApplicationInfo for the application to which this item belongs, 508 * if available, otherwise returns null. 509 * 510 * @return Returns the ApplicationInfo of this item, or null if not known. 511 * 512 * @hide 513 */ getApplicationInfo()514 public ApplicationInfo getApplicationInfo() { 515 return null; 516 } 517 518 public static class DisplayNameComparator 519 implements Comparator<PackageItemInfo> { DisplayNameComparator(PackageManager pm)520 public DisplayNameComparator(PackageManager pm) { 521 mPM = pm; 522 } 523 compare(PackageItemInfo aa, PackageItemInfo ab)524 public final int compare(PackageItemInfo aa, PackageItemInfo ab) { 525 CharSequence sa = aa.loadLabel(mPM); 526 if (sa == null) sa = aa.name; 527 CharSequence sb = ab.loadLabel(mPM); 528 if (sb == null) sb = ab.name; 529 return sCollator.compare(sa.toString(), sb.toString()); 530 } 531 532 private final Collator sCollator = Collator.getInstance(); 533 private final PackageManager mPM; 534 } 535 } 536