1 /* 2 * Copyright (C) 2012 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.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; 20 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; 21 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; 22 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; 23 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; 24 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; 25 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; 26 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS; 27 import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; 28 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; 29 30 import android.annotation.NonNull; 31 import android.annotation.Nullable; 32 import android.compat.annotation.UnsupportedAppUsage; 33 import android.content.ComponentName; 34 import android.content.pm.overlay.OverlayPaths; 35 import android.content.pm.parsing.ParsingPackageRead; 36 import android.content.pm.parsing.component.ParsedMainComponent; 37 import android.os.BaseBundle; 38 import android.os.Debug; 39 import android.os.PersistableBundle; 40 import android.text.TextUtils; 41 import android.util.ArrayMap; 42 import android.util.ArraySet; 43 import android.util.DebugUtils; 44 import android.util.Pair; 45 import android.util.Slog; 46 import android.util.TypedXmlPullParser; 47 import android.util.TypedXmlSerializer; 48 49 import com.android.internal.annotations.VisibleForTesting; 50 import com.android.internal.util.ArrayUtils; 51 52 import org.xmlpull.v1.XmlPullParser; 53 import org.xmlpull.v1.XmlPullParserException; 54 import org.xmlpull.v1.XmlSerializer; 55 56 import java.io.IOException; 57 import java.util.Map; 58 import java.util.Objects; 59 60 /** 61 * Per-user state information about a package. 62 * @hide 63 */ 64 public class PackageUserState { 65 private static final boolean DEBUG = false; 66 private static final String LOG_TAG = "PackageUserState"; 67 68 public long ceDataInode; 69 public boolean installed; 70 public boolean stopped; 71 public boolean notLaunched; 72 public boolean hidden; // Is the app restricted by owner / admin 73 public int distractionFlags; 74 public boolean suspended; 75 public ArrayMap<String, SuspendParams> suspendParams; // Suspending package to suspend params 76 public boolean instantApp; 77 public boolean virtualPreload; 78 public int enabled; 79 public String lastDisableAppCaller; 80 public int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED; 81 public int installReason; 82 public @PackageManager.UninstallReason int uninstallReason; 83 public String harmfulAppWarning; 84 public String splashScreenTheme; 85 86 public ArraySet<String> disabledComponents; 87 public ArraySet<String> enabledComponents; 88 89 private OverlayPaths overlayPaths; 90 // Maps library name to overlay paths. 91 private ArrayMap<String, OverlayPaths> sharedLibraryOverlayPaths; 92 private OverlayPaths cachedOverlayPaths; 93 94 @Nullable 95 private ArrayMap<ComponentName, Pair<String, Integer>> componentLabelIconOverrideMap; 96 97 @UnsupportedAppUsage PackageUserState()98 public PackageUserState() { 99 installed = true; 100 hidden = false; 101 suspended = false; 102 enabled = COMPONENT_ENABLED_STATE_DEFAULT; 103 installReason = PackageManager.INSTALL_REASON_UNKNOWN; 104 uninstallReason = PackageManager.UNINSTALL_REASON_UNKNOWN; 105 } 106 107 @VisibleForTesting PackageUserState(PackageUserState o)108 public PackageUserState(PackageUserState o) { 109 ceDataInode = o.ceDataInode; 110 installed = o.installed; 111 stopped = o.stopped; 112 notLaunched = o.notLaunched; 113 hidden = o.hidden; 114 distractionFlags = o.distractionFlags; 115 suspended = o.suspended; 116 suspendParams = new ArrayMap<>(o.suspendParams); 117 instantApp = o.instantApp; 118 virtualPreload = o.virtualPreload; 119 enabled = o.enabled; 120 lastDisableAppCaller = o.lastDisableAppCaller; 121 categoryHint = o.categoryHint; 122 installReason = o.installReason; 123 uninstallReason = o.uninstallReason; 124 disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents); 125 enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents); 126 overlayPaths = o.overlayPaths; 127 if (o.sharedLibraryOverlayPaths != null) { 128 sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths); 129 } 130 harmfulAppWarning = o.harmfulAppWarning; 131 if (o.componentLabelIconOverrideMap != null) { 132 this.componentLabelIconOverrideMap = new ArrayMap<>(o.componentLabelIconOverrideMap); 133 } 134 splashScreenTheme = o.splashScreenTheme; 135 } 136 137 @Nullable getOverlayPaths()138 public OverlayPaths getOverlayPaths() { 139 return overlayPaths; 140 } 141 142 @Nullable getSharedLibraryOverlayPaths()143 public Map<String, OverlayPaths> getSharedLibraryOverlayPaths() { 144 return sharedLibraryOverlayPaths; 145 } 146 147 /** 148 * Sets the path of overlays currently enabled for this package and user combination. 149 * @return true if the path contents differ than what they were previously 150 */ 151 @Nullable setOverlayPaths(@ullable OverlayPaths paths)152 public boolean setOverlayPaths(@Nullable OverlayPaths paths) { 153 if (Objects.equals(paths, overlayPaths)) { 154 return false; 155 } 156 if ((overlayPaths == null && paths.isEmpty()) 157 || (paths == null && overlayPaths.isEmpty())) { 158 return false; 159 } 160 overlayPaths = paths; 161 cachedOverlayPaths = null; 162 return true; 163 } 164 165 /** 166 * Sets the path of overlays currently enabled for a library that this package uses. 167 * 168 * @return true if the path contents for the library differ than what they were previously 169 */ setSharedLibraryOverlayPaths(@onNull String library, @Nullable OverlayPaths paths)170 public boolean setSharedLibraryOverlayPaths(@NonNull String library, 171 @Nullable OverlayPaths paths) { 172 if (sharedLibraryOverlayPaths == null) { 173 sharedLibraryOverlayPaths = new ArrayMap<>(); 174 } 175 final OverlayPaths currentPaths = sharedLibraryOverlayPaths.get(library); 176 if (Objects.equals(paths, currentPaths)) { 177 return false; 178 } 179 cachedOverlayPaths = null; 180 if (paths == null || paths.isEmpty()) { 181 return sharedLibraryOverlayPaths.remove(library) != null; 182 } else { 183 sharedLibraryOverlayPaths.put(library, paths); 184 return true; 185 } 186 } 187 188 /** 189 * Overrides the non-localized label and icon of a component. 190 * 191 * @return true if the label or icon was changed. 192 */ 193 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) overrideLabelAndIcon(@onNull ComponentName component, @Nullable String nonLocalizedLabel, @Nullable Integer icon)194 public boolean overrideLabelAndIcon(@NonNull ComponentName component, 195 @Nullable String nonLocalizedLabel, @Nullable Integer icon) { 196 String existingLabel = null; 197 Integer existingIcon = null; 198 199 if (componentLabelIconOverrideMap != null) { 200 Pair<String, Integer> pair = componentLabelIconOverrideMap.get(component); 201 if (pair != null) { 202 existingLabel = pair.first; 203 existingIcon = pair.second; 204 } 205 } 206 207 boolean changed = !TextUtils.equals(existingLabel, nonLocalizedLabel) 208 || !Objects.equals(existingIcon, icon); 209 210 if (changed) { 211 if (nonLocalizedLabel == null && icon == null) { 212 componentLabelIconOverrideMap.remove(component); 213 if (componentLabelIconOverrideMap.isEmpty()) { 214 componentLabelIconOverrideMap = null; 215 } 216 } else { 217 if (componentLabelIconOverrideMap == null) { 218 componentLabelIconOverrideMap = new ArrayMap<>(1); 219 } 220 221 componentLabelIconOverrideMap.put(component, Pair.create(nonLocalizedLabel, icon)); 222 } 223 } 224 225 return changed; 226 } 227 228 /** 229 * Clears all values previously set by {@link #overrideLabelAndIcon(ComponentName, 230 * String, Integer)}. 231 * 232 * This is done when the package is updated as the components and resource IDs may have changed. 233 */ resetOverrideComponentLabelIcon()234 public void resetOverrideComponentLabelIcon() { 235 componentLabelIconOverrideMap = null; 236 } 237 238 @Nullable getOverrideLabelIconForComponent(ComponentName componentName)239 public Pair<String, Integer> getOverrideLabelIconForComponent(ComponentName componentName) { 240 if (ArrayUtils.isEmpty(componentLabelIconOverrideMap)) { 241 return null; 242 } 243 244 return componentLabelIconOverrideMap.get(componentName); 245 } 246 247 248 /** 249 * Test if this package is installed. 250 */ isAvailable(int flags)251 public boolean isAvailable(int flags) { 252 // True if it is installed for this user and it is not hidden. If it is hidden, 253 // still return true if the caller requested MATCH_UNINSTALLED_PACKAGES 254 final boolean matchAnyUser = (flags & PackageManager.MATCH_ANY_USER) != 0; 255 final boolean matchUninstalled = (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0; 256 return matchAnyUser 257 || (this.installed 258 && (!this.hidden || matchUninstalled)); 259 } 260 isMatch(ComponentInfo componentInfo, int flags)261 public boolean isMatch(ComponentInfo componentInfo, int flags) { 262 return isMatch(componentInfo.applicationInfo.isSystemApp(), 263 componentInfo.applicationInfo.enabled, componentInfo.enabled, 264 componentInfo.directBootAware, componentInfo.name, flags); 265 } 266 isMatch(boolean isSystem, boolean isPackageEnabled, ParsedMainComponent component, int flags)267 public boolean isMatch(boolean isSystem, boolean isPackageEnabled, 268 ParsedMainComponent component, int flags) { 269 return isMatch(isSystem, isPackageEnabled, component.isEnabled(), 270 component.isDirectBootAware(), component.getName(), flags); 271 } 272 273 /** 274 * Test if the given component is considered installed, enabled and a match 275 * for the given flags. 276 * 277 * <p> 278 * Expects at least one of {@link PackageManager#MATCH_DIRECT_BOOT_AWARE} and 279 * {@link PackageManager#MATCH_DIRECT_BOOT_UNAWARE} are specified in {@code flags}. 280 * </p> 281 * 282 */ isMatch(boolean isSystem, boolean isPackageEnabled, boolean isComponentEnabled, boolean isComponentDirectBootAware, String componentName, int flags)283 public boolean isMatch(boolean isSystem, boolean isPackageEnabled, boolean isComponentEnabled, 284 boolean isComponentDirectBootAware, String componentName, int flags) { 285 final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0; 286 if (!isAvailable(flags) && !(isSystem && matchUninstalled)) { 287 return reportIfDebug(false, flags); 288 } 289 290 if (!isEnabled(isPackageEnabled, isComponentEnabled, componentName, flags)) { 291 return reportIfDebug(false, flags); 292 } 293 294 if ((flags & MATCH_SYSTEM_ONLY) != 0) { 295 if (!isSystem) { 296 return reportIfDebug(false, flags); 297 } 298 } 299 300 final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0) 301 && !isComponentDirectBootAware; 302 final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0) 303 && isComponentDirectBootAware; 304 return reportIfDebug(matchesUnaware || matchesAware, flags); 305 } 306 reportIfDebug(boolean result, int flags)307 public boolean reportIfDebug(boolean result, int flags) { 308 if (DEBUG && !result) { 309 Slog.i(LOG_TAG, "No match!; flags: " 310 + DebugUtils.flagsToString(PackageManager.class, "MATCH_", flags) + " " 311 + Debug.getCaller()); 312 } 313 return result; 314 } 315 isPackageEnabled(@onNull ParsingPackageRead pkg)316 public boolean isPackageEnabled(@NonNull ParsingPackageRead pkg) { 317 switch (this.enabled) { 318 case COMPONENT_ENABLED_STATE_ENABLED: 319 return true; 320 case COMPONENT_ENABLED_STATE_DISABLED: 321 case COMPONENT_ENABLED_STATE_DISABLED_USER: 322 case COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED: 323 return false; 324 default: 325 case COMPONENT_ENABLED_STATE_DEFAULT: 326 return pkg.isEnabled(); 327 } 328 } 329 isEnabled(ComponentInfo componentInfo, int flags)330 public boolean isEnabled(ComponentInfo componentInfo, int flags) { 331 return isEnabled(componentInfo.applicationInfo.enabled, componentInfo.enabled, 332 componentInfo.name, flags); 333 } 334 isEnabled(boolean isPackageEnabled, ParsedMainComponent parsedComponent, int flags)335 public boolean isEnabled(boolean isPackageEnabled, 336 ParsedMainComponent parsedComponent, int flags) { 337 return isEnabled(isPackageEnabled, parsedComponent.isEnabled(), parsedComponent.getName(), 338 flags); 339 } 340 341 /** 342 * Test if the given component is considered enabled. 343 */ isEnabled(boolean isPackageEnabled, boolean isComponentEnabled, String componentName, int flags)344 public boolean isEnabled(boolean isPackageEnabled, boolean isComponentEnabled, 345 String componentName, int flags) { 346 if ((flags & MATCH_DISABLED_COMPONENTS) != 0) { 347 return true; 348 } 349 350 // First check if the overall package is disabled; if the package is 351 // enabled then fall through to check specific component 352 switch (this.enabled) { 353 case COMPONENT_ENABLED_STATE_DISABLED: 354 case COMPONENT_ENABLED_STATE_DISABLED_USER: 355 return false; 356 case COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED: 357 if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) { 358 return false; 359 } 360 // fallthrough 361 case COMPONENT_ENABLED_STATE_DEFAULT: 362 if (!isPackageEnabled) { 363 return false; 364 } 365 // fallthrough 366 case COMPONENT_ENABLED_STATE_ENABLED: 367 break; 368 } 369 370 // Check if component has explicit state before falling through to 371 // the manifest default 372 if (ArrayUtils.contains(this.enabledComponents, componentName)) { 373 return true; 374 } 375 if (ArrayUtils.contains(this.disabledComponents, componentName)) { 376 return false; 377 } 378 379 return isComponentEnabled; 380 } 381 getAllOverlayPaths()382 public OverlayPaths getAllOverlayPaths() { 383 if (overlayPaths == null && sharedLibraryOverlayPaths == null) { 384 return null; 385 } 386 if (cachedOverlayPaths != null) { 387 return cachedOverlayPaths; 388 } 389 final OverlayPaths.Builder newPaths = new OverlayPaths.Builder(); 390 newPaths.addAll(overlayPaths); 391 if (sharedLibraryOverlayPaths != null) { 392 for (final OverlayPaths libOverlayPaths : sharedLibraryOverlayPaths.values()) { 393 newPaths.addAll(libOverlayPaths); 394 } 395 } 396 cachedOverlayPaths = newPaths.build(); 397 return cachedOverlayPaths; 398 } 399 400 @Override equals(@ullable Object obj)401 final public boolean equals(@Nullable Object obj) { 402 if (!(obj instanceof PackageUserState)) { 403 return false; 404 } 405 final PackageUserState oldState = (PackageUserState) obj; 406 if (ceDataInode != oldState.ceDataInode) { 407 return false; 408 } 409 if (installed != oldState.installed) { 410 return false; 411 } 412 if (stopped != oldState.stopped) { 413 return false; 414 } 415 if (notLaunched != oldState.notLaunched) { 416 return false; 417 } 418 if (hidden != oldState.hidden) { 419 return false; 420 } 421 if (distractionFlags != oldState.distractionFlags) { 422 return false; 423 } 424 if (suspended != oldState.suspended) { 425 return false; 426 } 427 if (suspended) { 428 if (!Objects.equals(suspendParams, oldState.suspendParams)) { 429 return false; 430 } 431 } 432 if (instantApp != oldState.instantApp) { 433 return false; 434 } 435 if (virtualPreload != oldState.virtualPreload) { 436 return false; 437 } 438 if (enabled != oldState.enabled) { 439 return false; 440 } 441 if ((lastDisableAppCaller == null && oldState.lastDisableAppCaller != null) 442 || (lastDisableAppCaller != null 443 && !lastDisableAppCaller.equals(oldState.lastDisableAppCaller))) { 444 return false; 445 } 446 if (categoryHint != oldState.categoryHint) { 447 return false; 448 } 449 if (installReason != oldState.installReason) { 450 return false; 451 } 452 if (uninstallReason != oldState.uninstallReason) { 453 return false; 454 } 455 if ((disabledComponents == null && oldState.disabledComponents != null) 456 || (disabledComponents != null && oldState.disabledComponents == null)) { 457 return false; 458 } 459 if (disabledComponents != null) { 460 if (disabledComponents.size() != oldState.disabledComponents.size()) { 461 return false; 462 } 463 for (int i = disabledComponents.size() - 1; i >=0; --i) { 464 if (!oldState.disabledComponents.contains(disabledComponents.valueAt(i))) { 465 return false; 466 } 467 } 468 } 469 if ((enabledComponents == null && oldState.enabledComponents != null) 470 || (enabledComponents != null && oldState.enabledComponents == null)) { 471 return false; 472 } 473 if (enabledComponents != null) { 474 if (enabledComponents.size() != oldState.enabledComponents.size()) { 475 return false; 476 } 477 for (int i = enabledComponents.size() - 1; i >=0; --i) { 478 if (!oldState.enabledComponents.contains(enabledComponents.valueAt(i))) { 479 return false; 480 } 481 } 482 } 483 if (harmfulAppWarning == null && oldState.harmfulAppWarning != null 484 || (harmfulAppWarning != null 485 && !harmfulAppWarning.equals(oldState.harmfulAppWarning))) { 486 return false; 487 } 488 489 if (!Objects.equals(splashScreenTheme, oldState.splashScreenTheme)) { 490 return false; 491 } 492 return true; 493 } 494 495 @Override hashCode()496 public int hashCode() { 497 int hashCode = Long.hashCode(ceDataInode); 498 hashCode = 31 * hashCode + Boolean.hashCode(installed); 499 hashCode = 31 * hashCode + Boolean.hashCode(stopped); 500 hashCode = 31 * hashCode + Boolean.hashCode(notLaunched); 501 hashCode = 31 * hashCode + Boolean.hashCode(hidden); 502 hashCode = 31 * hashCode + distractionFlags; 503 hashCode = 31 * hashCode + Boolean.hashCode(suspended); 504 hashCode = 31 * hashCode + Objects.hashCode(suspendParams); 505 hashCode = 31 * hashCode + Boolean.hashCode(instantApp); 506 hashCode = 31 * hashCode + Boolean.hashCode(virtualPreload); 507 hashCode = 31 * hashCode + enabled; 508 hashCode = 31 * hashCode + Objects.hashCode(lastDisableAppCaller); 509 hashCode = 31 * hashCode + categoryHint; 510 hashCode = 31 * hashCode + installReason; 511 hashCode = 31 * hashCode + uninstallReason; 512 hashCode = 31 * hashCode + Objects.hashCode(disabledComponents); 513 hashCode = 31 * hashCode + Objects.hashCode(enabledComponents); 514 hashCode = 31 * hashCode + Objects.hashCode(harmfulAppWarning); 515 hashCode = 31 * hashCode + Objects.hashCode(splashScreenTheme); 516 return hashCode; 517 } 518 519 /** 520 * Container to describe suspension parameters. 521 */ 522 public static final class SuspendParams { 523 private static final String TAG_DIALOG_INFO = "dialog-info"; 524 private static final String TAG_APP_EXTRAS = "app-extras"; 525 private static final String TAG_LAUNCHER_EXTRAS = "launcher-extras"; 526 527 public SuspendDialogInfo dialogInfo; 528 public PersistableBundle appExtras; 529 public PersistableBundle launcherExtras; 530 SuspendParams()531 private SuspendParams() { 532 } 533 534 /** 535 * Returns a {@link SuspendParams} object with the given fields. Returns {@code null} if all 536 * the fields are {@code null}. 537 * 538 * @param dialogInfo 539 * @param appExtras 540 * @param launcherExtras 541 * @return A {@link SuspendParams} object or {@code null}. 542 */ getInstanceOrNull(SuspendDialogInfo dialogInfo, PersistableBundle appExtras, PersistableBundle launcherExtras)543 public static SuspendParams getInstanceOrNull(SuspendDialogInfo dialogInfo, 544 PersistableBundle appExtras, PersistableBundle launcherExtras) { 545 if (dialogInfo == null && appExtras == null && launcherExtras == null) { 546 return null; 547 } 548 final SuspendParams instance = new SuspendParams(); 549 instance.dialogInfo = dialogInfo; 550 instance.appExtras = appExtras; 551 instance.launcherExtras = launcherExtras; 552 return instance; 553 } 554 555 @Override equals(@ullable Object obj)556 public boolean equals(@Nullable Object obj) { 557 if (this == obj) { 558 return true; 559 } 560 if (!(obj instanceof SuspendParams)) { 561 return false; 562 } 563 final SuspendParams other = (SuspendParams) obj; 564 if (!Objects.equals(dialogInfo, other.dialogInfo)) { 565 return false; 566 } 567 if (!BaseBundle.kindofEquals(appExtras, other.appExtras)) { 568 return false; 569 } 570 if (!BaseBundle.kindofEquals(launcherExtras, other.launcherExtras)) { 571 return false; 572 } 573 return true; 574 } 575 576 @Override hashCode()577 public int hashCode() { 578 int hashCode = Objects.hashCode(dialogInfo); 579 hashCode = 31 * hashCode + ((appExtras != null) ? appExtras.size() : 0); 580 hashCode = 31 * hashCode + ((launcherExtras != null) ? launcherExtras.size() : 0); 581 return hashCode; 582 } 583 584 /** 585 * Serializes this object into an xml format 586 * @param out the {@link XmlSerializer} object 587 * @throws IOException 588 */ saveToXml(TypedXmlSerializer out)589 public void saveToXml(TypedXmlSerializer out) throws IOException { 590 if (dialogInfo != null) { 591 out.startTag(null, TAG_DIALOG_INFO); 592 dialogInfo.saveToXml(out); 593 out.endTag(null, TAG_DIALOG_INFO); 594 } 595 if (appExtras != null) { 596 out.startTag(null, TAG_APP_EXTRAS); 597 try { 598 appExtras.saveToXml(out); 599 } catch (XmlPullParserException e) { 600 Slog.e(LOG_TAG, "Exception while trying to write appExtras." 601 + " Will be lost on reboot", e); 602 } 603 out.endTag(null, TAG_APP_EXTRAS); 604 } 605 if (launcherExtras != null) { 606 out.startTag(null, TAG_LAUNCHER_EXTRAS); 607 try { 608 launcherExtras.saveToXml(out); 609 } catch (XmlPullParserException e) { 610 Slog.e(LOG_TAG, "Exception while trying to write launcherExtras." 611 + " Will be lost on reboot", e); 612 } 613 out.endTag(null, TAG_LAUNCHER_EXTRAS); 614 } 615 } 616 617 /** 618 * Parses this object from the xml format. Returns {@code null} if no object related 619 * information could be read. 620 * @param in the reader 621 * @return 622 */ restoreFromXml(TypedXmlPullParser in)623 public static SuspendParams restoreFromXml(TypedXmlPullParser in) throws IOException { 624 SuspendDialogInfo readDialogInfo = null; 625 PersistableBundle readAppExtras = null; 626 PersistableBundle readLauncherExtras = null; 627 628 final int currentDepth = in.getDepth(); 629 int type; 630 try { 631 while ((type = in.next()) != XmlPullParser.END_DOCUMENT 632 && (type != XmlPullParser.END_TAG 633 || in.getDepth() > currentDepth)) { 634 if (type == XmlPullParser.END_TAG 635 || type == XmlPullParser.TEXT) { 636 continue; 637 } 638 switch (in.getName()) { 639 case TAG_DIALOG_INFO: 640 readDialogInfo = SuspendDialogInfo.restoreFromXml(in); 641 break; 642 case TAG_APP_EXTRAS: 643 readAppExtras = PersistableBundle.restoreFromXml(in); 644 break; 645 case TAG_LAUNCHER_EXTRAS: 646 readLauncherExtras = PersistableBundle.restoreFromXml(in); 647 break; 648 default: 649 Slog.w(LOG_TAG, "Unknown tag " + in.getName() 650 + " in SuspendParams. Ignoring"); 651 break; 652 } 653 } 654 } catch (XmlPullParserException e) { 655 Slog.e(LOG_TAG, "Exception while trying to parse SuspendParams," 656 + " some fields may default", e); 657 } 658 return getInstanceOrNull(readDialogInfo, readAppExtras, readLauncherExtras); 659 } 660 } 661 } 662