1 /*
2  * Copyright (C) 2021 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 com.android.server.pm.pkg;
18 
19 import android.annotation.Nullable;
20 import android.content.pm.SuspendDialogInfo;
21 import android.os.BaseBundle;
22 import android.os.PersistableBundle;
23 import android.util.Slog;
24 
25 import com.android.modules.utils.TypedXmlPullParser;
26 import com.android.modules.utils.TypedXmlSerializer;
27 
28 import org.xmlpull.v1.XmlPullParser;
29 import org.xmlpull.v1.XmlPullParserException;
30 import org.xmlpull.v1.XmlSerializer;
31 
32 import java.io.IOException;
33 import java.util.Objects;
34 
35 /**
36  * Container to describe suspension parameters.
37  * @hide
38  */
39 public final class SuspendParams {
40 
41     private static final String LOG_TAG = "FrameworkPackageUserState";
42     private static final String TAG_DIALOG_INFO = "dialog-info";
43     private static final String TAG_APP_EXTRAS = "app-extras";
44     private static final String TAG_LAUNCHER_EXTRAS = "launcher-extras";
45     private static final String ATTR_QUARANTINED = "quarantined";
46 
47     private final SuspendDialogInfo mDialogInfo;
48     private final PersistableBundle mAppExtras;
49     private final PersistableBundle mLauncherExtras;
50 
51     private final boolean mQuarantined;
52 
SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras, PersistableBundle launcherExtras)53     public SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras,
54             PersistableBundle launcherExtras) {
55         this(dialogInfo, appExtras, launcherExtras, false /* quarantined */);
56     }
57 
SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras, PersistableBundle launcherExtras, boolean quarantined)58     public SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras,
59             PersistableBundle launcherExtras, boolean quarantined) {
60         this.mDialogInfo = dialogInfo;
61         this.mAppExtras = appExtras;
62         this.mLauncherExtras = launcherExtras;
63         this.mQuarantined = quarantined;
64     }
65 
66     @Override
equals(@ullable Object obj)67     public boolean equals(@Nullable Object obj) {
68         if (this == obj) {
69             return true;
70         }
71         if (!(obj instanceof SuspendParams)) {
72             return false;
73         }
74         final SuspendParams other = (SuspendParams) obj;
75         if (!Objects.equals(mDialogInfo, other.mDialogInfo)) {
76             return false;
77         }
78         if (!BaseBundle.kindofEquals(mAppExtras, other.mAppExtras)) {
79             return false;
80         }
81         if (!BaseBundle.kindofEquals(mLauncherExtras, other.mLauncherExtras)) {
82             return false;
83         }
84         if (mQuarantined != other.mQuarantined) {
85             return false;
86         }
87         return true;
88     }
89 
90     @Override
hashCode()91     public int hashCode() {
92         int hashCode = Objects.hashCode(mDialogInfo);
93         hashCode = 31 * hashCode + ((mAppExtras != null) ? mAppExtras.size() : 0);
94         hashCode = 31 * hashCode + ((mLauncherExtras != null) ? mLauncherExtras.size() : 0);
95         hashCode = 31 * hashCode + Boolean.hashCode(mQuarantined);
96         return hashCode;
97     }
98 
99     /**
100      * Serializes this object into an xml format
101      *
102      * @param out the {@link XmlSerializer} object
103      */
saveToXml(TypedXmlSerializer out)104     public void saveToXml(TypedXmlSerializer out) throws IOException {
105         out.attributeBoolean(null, ATTR_QUARANTINED, mQuarantined);
106         if (mDialogInfo != null) {
107             out.startTag(null, TAG_DIALOG_INFO);
108             mDialogInfo.saveToXml(out);
109             out.endTag(null, TAG_DIALOG_INFO);
110         }
111         if (mAppExtras != null) {
112             out.startTag(null, TAG_APP_EXTRAS);
113             try {
114                 mAppExtras.saveToXml(out);
115             } catch (XmlPullParserException e) {
116                 Slog.e(LOG_TAG, "Exception while trying to write appExtras."
117                         + " Will be lost on reboot", e);
118             }
119             out.endTag(null, TAG_APP_EXTRAS);
120         }
121         if (mLauncherExtras != null) {
122             out.startTag(null, TAG_LAUNCHER_EXTRAS);
123             try {
124                 mLauncherExtras.saveToXml(out);
125             } catch (XmlPullParserException e) {
126                 Slog.e(LOG_TAG, "Exception while trying to write launcherExtras."
127                         + " Will be lost on reboot", e);
128             }
129             out.endTag(null, TAG_LAUNCHER_EXTRAS);
130         }
131     }
132 
133     /**
134      * Parses this object from the xml format. Returns {@code null} if no object related
135      * information could be read.
136      *
137      * @param in the reader
138      */
restoreFromXml(TypedXmlPullParser in)139     public static SuspendParams restoreFromXml(TypedXmlPullParser in) throws IOException {
140         SuspendDialogInfo readDialogInfo = null;
141         PersistableBundle readAppExtras = null;
142         PersistableBundle readLauncherExtras = null;
143 
144         final boolean quarantined = in.getAttributeBoolean(null, ATTR_QUARANTINED, false);
145 
146         final int currentDepth = in.getDepth();
147         int type;
148         try {
149             while ((type = in.next()) != XmlPullParser.END_DOCUMENT
150                     && (type != XmlPullParser.END_TAG
151                     || in.getDepth() > currentDepth)) {
152                 if (type == XmlPullParser.END_TAG
153                         || type == XmlPullParser.TEXT) {
154                     continue;
155                 }
156                 switch (in.getName()) {
157                     case TAG_DIALOG_INFO:
158                         readDialogInfo = SuspendDialogInfo.restoreFromXml(in);
159                         break;
160                     case TAG_APP_EXTRAS:
161                         readAppExtras = PersistableBundle.restoreFromXml(in);
162                         break;
163                     case TAG_LAUNCHER_EXTRAS:
164                         readLauncherExtras = PersistableBundle.restoreFromXml(in);
165                         break;
166                     default:
167                         Slog.w(LOG_TAG, "Unknown tag " + in.getName()
168                                 + " in SuspendParams. Ignoring");
169                         break;
170                 }
171             }
172         } catch (XmlPullParserException e) {
173             Slog.e(LOG_TAG, "Exception while trying to parse SuspendParams,"
174                     + " some fields may default", e);
175         }
176         return new SuspendParams(readDialogInfo, readAppExtras, readLauncherExtras, quarantined);
177     }
178 
getDialogInfo()179     public SuspendDialogInfo getDialogInfo() {
180         return mDialogInfo;
181     }
182 
getAppExtras()183     public PersistableBundle getAppExtras() {
184         return mAppExtras;
185     }
186 
getLauncherExtras()187     public PersistableBundle getLauncherExtras() {
188         return mLauncherExtras;
189     }
190 
isQuarantined()191     public boolean isQuarantined() {
192         return mQuarantined;
193     }
194 }
195