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