• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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