1 /*
2  * Copyright (C) 2019 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.app.admin;
18 
19 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
20 import static org.xmlpull.v1.XmlPullParser.END_TAG;
21 import static org.xmlpull.v1.XmlPullParser.TEXT;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.content.ComponentName;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.util.IndentingPrintWriter;
29 import android.util.Log;
30 
31 import com.android.modules.utils.TypedXmlPullParser;
32 import com.android.modules.utils.TypedXmlSerializer;
33 
34 import org.xmlpull.v1.XmlPullParserException;
35 
36 import java.io.IOException;
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 /**
41  * The factory reset protection policy determines which accounts can unlock a device that
42  * has gone through untrusted factory reset.
43  * <p>
44  * Only a device owner or profile owner of an organization-owned device can set a factory
45  * reset protection policy for the device by calling the {@code DevicePolicyManager} method
46  * {@link DevicePolicyManager#setFactoryResetProtectionPolicy(ComponentName,
47  * FactoryResetProtectionPolicy)}}.
48  * <p>
49  * Normally factory reset protection does not kick in if the device is factory reset via Settings.
50  * This is also the case when a device owner sets factory reset protection policy. However,
51  * when a profile owner of an organization-owned device sets factory reset protection policy that
52  * locks the device to specific accounts, the policy will take effect even if factory reset is
53  * performed from Settings.
54  *
55  * @see DevicePolicyManager#setFactoryResetProtectionPolicy
56  * @see DevicePolicyManager#getFactoryResetProtectionPolicy
57  */
58 public final class FactoryResetProtectionPolicy implements Parcelable {
59 
60     private static final String LOG_TAG = "FactoryResetProtectionPolicy";
61 
62     private static final String KEY_FACTORY_RESET_PROTECTION_ACCOUNT =
63             "factory_reset_protection_account";
64     private static final String KEY_FACTORY_RESET_PROTECTION_ENABLED =
65             "factory_reset_protection_enabled";
66     private static final String ATTR_VALUE = "value";
67 
68     private final List<String> mFactoryResetProtectionAccounts;
69     private final boolean mFactoryResetProtectionEnabled;
70 
FactoryResetProtectionPolicy(List<String> factoryResetProtectionAccounts, boolean factoryResetProtectionEnabled)71     private FactoryResetProtectionPolicy(List<String> factoryResetProtectionAccounts,
72             boolean factoryResetProtectionEnabled) {
73         mFactoryResetProtectionAccounts = factoryResetProtectionAccounts;
74         mFactoryResetProtectionEnabled = factoryResetProtectionEnabled;
75     }
76 
77     /**
78      * Get the list of accounts that can provision a device which has been factory reset.
79      */
getFactoryResetProtectionAccounts()80     public @NonNull List<String> getFactoryResetProtectionAccounts() {
81         return mFactoryResetProtectionAccounts;
82     }
83 
84     /**
85      * Return whether factory reset protection for the device is enabled or not.
86      */
isFactoryResetProtectionEnabled()87     public boolean isFactoryResetProtectionEnabled() {
88         return mFactoryResetProtectionEnabled;
89     }
90 
91     /**
92      * Builder class for {@link FactoryResetProtectionPolicy} objects.
93      */
94     public static class Builder {
95         private List<String> mFactoryResetProtectionAccounts;
96         private boolean mFactoryResetProtectionEnabled;
97 
98         /**
99          * Initialize a new Builder to construct a {@link FactoryResetProtectionPolicy}.
100          */
Builder()101         public Builder() {
102             mFactoryResetProtectionEnabled = true;
103         };
104 
105         /**
106          * Sets which accounts can unlock a device that has been factory reset.
107          * <p>
108          * Once set, the consumer unlock flow will be disabled and only accounts in this list
109          * can unlock factory reset protection after untrusted factory reset.
110          * <p>
111          * It's up to the FRP management agent to interpret the {@code String} as account it
112          * supports. Please consult their relevant documentation for details.
113          *
114          * @param factoryResetProtectionAccounts list of accounts.
115          * @return the same Builder instance.
116          */
117         @NonNull
setFactoryResetProtectionAccounts( @onNull List<String> factoryResetProtectionAccounts)118         public Builder setFactoryResetProtectionAccounts(
119                 @NonNull List<String> factoryResetProtectionAccounts) {
120             mFactoryResetProtectionAccounts = new ArrayList<>(factoryResetProtectionAccounts);
121             return this;
122         }
123 
124         /**
125          * Sets whether factory reset protection is enabled or not.
126          * <p>
127          * Once disabled, factory reset protection will not kick in all together when the device
128          * goes through untrusted factory reset. This applies to both the consumer unlock flow and
129          * the admin account overrides via {@link #setFactoryResetProtectionAccounts}. By default,
130          * factory reset protection is enabled.
131          *
132          * @param factoryResetProtectionEnabled Whether the policy is enabled or not.
133          * @return the same Builder instance.
134          */
135         @NonNull
setFactoryResetProtectionEnabled(boolean factoryResetProtectionEnabled)136         public Builder setFactoryResetProtectionEnabled(boolean factoryResetProtectionEnabled) {
137             mFactoryResetProtectionEnabled = factoryResetProtectionEnabled;
138             return this;
139         }
140 
141         /**
142          * Combines all of the attributes that have been set on this {@code Builder}
143          *
144          * @return a new {@link FactoryResetProtectionPolicy} object.
145          */
146         @NonNull
build()147         public FactoryResetProtectionPolicy build() {
148             return new FactoryResetProtectionPolicy(mFactoryResetProtectionAccounts,
149                     mFactoryResetProtectionEnabled);
150         }
151     }
152 
153     @Override
toString()154     public String toString() {
155         return "FactoryResetProtectionPolicy{"
156                 + "mFactoryResetProtectionAccounts=" + mFactoryResetProtectionAccounts
157                 + ", mFactoryResetProtectionEnabled=" + mFactoryResetProtectionEnabled
158                 + '}';
159     }
160 
161     @Override
writeToParcel(@onNull Parcel dest, @Nullable int flags)162     public void writeToParcel(@NonNull Parcel dest, @Nullable int flags) {
163         int accountsCount = mFactoryResetProtectionAccounts.size();
164         dest.writeInt(accountsCount);
165         for (String account: mFactoryResetProtectionAccounts) {
166             dest.writeString(account);
167         }
168         dest.writeBoolean(mFactoryResetProtectionEnabled);
169     }
170 
171     @Override
describeContents()172     public int describeContents() {
173         return 0;
174     }
175 
176     public static final @NonNull Creator<FactoryResetProtectionPolicy> CREATOR =
177             new Creator<FactoryResetProtectionPolicy>() {
178 
179                 @Override
180                 public FactoryResetProtectionPolicy createFromParcel(Parcel in) {
181                     List<String> factoryResetProtectionAccounts = new ArrayList<>();
182                     int accountsCount = in.readInt();
183                     for (int i = 0; i < accountsCount; i++) {
184                         factoryResetProtectionAccounts.add(in.readString());
185                     }
186                     boolean factoryResetProtectionEnabled = in.readBoolean();
187 
188                     return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts,
189                             factoryResetProtectionEnabled);
190                 }
191 
192                 @Override
193                 public FactoryResetProtectionPolicy[] newArray(int size) {
194                     return new FactoryResetProtectionPolicy[size];
195                 }
196     };
197 
198     /**
199      * Restore a previously saved FactoryResetProtectionPolicy from XML.
200      * <p>
201      * No validation is required on the reconstructed policy since the XML was previously
202      * created by the system server from a validated policy.
203      * @hide
204      */
205     @Nullable
readFromXml(@onNull TypedXmlPullParser parser)206     public static FactoryResetProtectionPolicy readFromXml(@NonNull TypedXmlPullParser parser) {
207         try {
208             boolean factoryResetProtectionEnabled = parser.getAttributeBoolean(null,
209                     KEY_FACTORY_RESET_PROTECTION_ENABLED, false);
210 
211             List<String> factoryResetProtectionAccounts = new ArrayList<>();
212             int outerDepth = parser.getDepth();
213             int type;
214             while ((type = parser.next()) != END_DOCUMENT
215                     && (type != END_TAG || parser.getDepth() > outerDepth)) {
216                 if (type == END_TAG || type == TEXT) {
217                     continue;
218                 }
219                 if (!parser.getName().equals(KEY_FACTORY_RESET_PROTECTION_ACCOUNT)) {
220                     continue;
221                 }
222                 factoryResetProtectionAccounts.add(
223                         parser.getAttributeValue(null, ATTR_VALUE));
224             }
225 
226             return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts,
227                     factoryResetProtectionEnabled);
228         } catch (XmlPullParserException | IOException e) {
229             Log.w(LOG_TAG, "Reading from xml failed", e);
230         }
231         return null;
232     }
233 
234     /**
235      * @hide
236      */
writeToXml(@onNull TypedXmlSerializer out)237     public void writeToXml(@NonNull TypedXmlSerializer out) throws IOException {
238         out.attributeBoolean(null, KEY_FACTORY_RESET_PROTECTION_ENABLED,
239                 mFactoryResetProtectionEnabled);
240         for (String account : mFactoryResetProtectionAccounts) {
241             out.startTag(null, KEY_FACTORY_RESET_PROTECTION_ACCOUNT);
242             out.attribute(null, ATTR_VALUE, account);
243             out.endTag(null, KEY_FACTORY_RESET_PROTECTION_ACCOUNT);
244         }
245     }
246 
247     /**
248      * Returns if the policy will result in factory reset protection being locked to
249      * admin-specified accounts.
250      * <p>
251      * When a device has a non-empty factory reset protection policy, trusted factory reset
252      * via Settings will no longer remove factory reset protection from the device.
253      * @hide
254      */
isNotEmpty()255     public boolean isNotEmpty() {
256         return !mFactoryResetProtectionAccounts.isEmpty() && mFactoryResetProtectionEnabled;
257     }
258 
259     /**
260      * @hide
261      */
dump(IndentingPrintWriter pw)262     public void dump(IndentingPrintWriter pw) {
263         pw.print("factoryResetProtectionEnabled=");
264         pw.println(mFactoryResetProtectionEnabled);
265 
266         pw.print("factoryResetProtectionAccounts=");
267         pw.increaseIndent();
268         for (int i = 0; i < mFactoryResetProtectionAccounts.size(); i++) {
269             pw.println(mFactoryResetProtectionAccounts.get(i));
270         }
271         pw.decreaseIndent();
272     }
273 }
274