blob: dd7284540fefd8e5b87d1eb669d3723736987249 [file] [log] [blame]
Rubin Xu8027a4f2015-03-10 17:52:37 +00001/*
2 * Copyright (C) 2015 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
17package android.app.admin;
18
Rubin Xu29b9a7d2018-01-11 09:24:02 +000019import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
20import static org.xmlpull.v1.XmlPullParser.END_TAG;
21import static org.xmlpull.v1.XmlPullParser.TEXT;
22
Rubin Xu8027a4f2015-03-10 17:52:37 +000023import android.annotation.IntDef;
Rubin Xu658d7bb2018-02-07 14:51:43 +000024import android.annotation.SystemApi;
Rubin Xud86d58c2015-05-05 16:57:37 +010025import android.os.Parcel;
26import android.os.Parcelable;
Rubin Xu29b9a7d2018-01-11 09:24:02 +000027import android.util.Log;
28import android.util.Pair;
Rubin Xu8027a4f2015-03-10 17:52:37 +000029
Rubin Xud86d58c2015-05-05 16:57:37 +010030import org.xmlpull.v1.XmlPullParser;
Rubin Xu29b9a7d2018-01-11 09:24:02 +000031import org.xmlpull.v1.XmlPullParserException;
Rubin Xud86d58c2015-05-05 16:57:37 +010032import org.xmlpull.v1.XmlSerializer;
33
34import java.io.IOException;
Rubin Xu8027a4f2015-03-10 17:52:37 +000035import java.lang.annotation.Retention;
36import java.lang.annotation.RetentionPolicy;
Rubin Xu658d7bb2018-02-07 14:51:43 +000037import java.time.Instant;
Rubin Xu29b9a7d2018-01-11 09:24:02 +000038import java.time.LocalDate;
Rubin Xu658d7bb2018-02-07 14:51:43 +000039import java.time.LocalDateTime;
40import java.time.LocalTime;
Rubin Xu1b2f3742018-03-28 14:54:08 +010041import java.time.MonthDay;
Rubin Xu658d7bb2018-02-07 14:51:43 +000042import java.time.ZoneId;
Rubin Xu29b9a7d2018-01-11 09:24:02 +000043import java.util.ArrayList;
Rubin Xu658d7bb2018-02-07 14:51:43 +000044import java.util.Calendar;
Rubin Xu1b2f3742018-03-28 14:54:08 +010045import java.util.Collections;
Rubin Xu29b9a7d2018-01-11 09:24:02 +000046import java.util.List;
Rubin Xu658d7bb2018-02-07 14:51:43 +000047import java.util.concurrent.TimeUnit;
Rubin Xu29b9a7d2018-01-11 09:24:02 +000048import java.util.stream.Collectors;
Rubin Xu8027a4f2015-03-10 17:52:37 +000049
50/**
Rubin Xufcf3d6e2018-04-18 15:18:12 +010051 * Determines when over-the-air system updates are installed on a device. Only a device policy
52 * controller (DPC) running in device owner mode can set an update policy for the device—by calling
53 * the {@code DevicePolicyManager} method
54 * {@link DevicePolicyManager#setSystemUpdatePolicy setSystemUpdatePolicy()}. An update
55 * policy affects the pending system update (if there is one) and any future updates for the device.
56 *
57 * <p>If a policy is set on a device, the system doesn't notify the user about updates.</p>
58 * <h3>Example</h3>
59 *
60 * <p>The example below shows how a DPC might set a maintenance window for system updates:</p>
61 * <pre><code>
62 * private final MAINTENANCE_WINDOW_START = 1380; // 11pm
63 * private final MAINTENANCE_WINDOW_END = 120; // 2am
64 *
65 * // ...
66 *
67 * // Create the system update policy
68 * SystemUpdatePolicy policy = SystemUpdatePolicy.createWindowedInstallPolicy(
69 * MAINTENANCE_WINDOW_START, MAINTENANCE_WINDOW_END);
70 *
71 * // Get a DevicePolicyManager instance to set the policy on the device
72 * DevicePolicyManager dpm =
73 * (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
74 * ComponentName adminComponent = getComponentName(context);
75 * dpm.setSystemUpdatePolicy(adminComponent, policy);
76 * </code></pre>
77 *
78 * <h3>Developer guide</h3>
Benjamin Miller625f91a2018-08-03 10:51:01 +000079 * To learn more, read <a href="{@docRoot}work/dpc/system-updates">Manage system updates</a>.
Rubin Xu8027a4f2015-03-10 17:52:37 +000080 *
Rubin Xu5faad8e2015-04-20 17:43:48 +010081 * @see DevicePolicyManager#setSystemUpdatePolicy
82 * @see DevicePolicyManager#getSystemUpdatePolicy
Rubin Xu8027a4f2015-03-10 17:52:37 +000083 */
Rubin Xu1b2f3742018-03-28 14:54:08 +010084public final class SystemUpdatePolicy implements Parcelable {
Rubin Xu29b9a7d2018-01-11 09:24:02 +000085 private static final String TAG = "SystemUpdatePolicy";
Rubin Xu8027a4f2015-03-10 17:52:37 +000086
87 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -070088 @IntDef(prefix = { "TYPE_" }, value = {
89 TYPE_INSTALL_AUTOMATIC,
90 TYPE_INSTALL_WINDOWED,
91 TYPE_POSTPONE
92 })
Rubin Xu8027a4f2015-03-10 17:52:37 +000093 @Retention(RetentionPolicy.SOURCE)
Rubin Xu5faad8e2015-04-20 17:43:48 +010094 @interface SystemUpdatePolicyType {}
Rubin Xu8027a4f2015-03-10 17:52:37 +000095
96 /**
Rubin Xud86d58c2015-05-05 16:57:37 +010097 * Unknown policy type, used only internally.
98 */
99 private static final int TYPE_UNKNOWN = -1;
Charles He57ed46a2017-01-03 11:48:07 +0000100
Rubin Xud86d58c2015-05-05 16:57:37 +0100101 /**
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100102 * Installs system updates (without user interaction) as soon as they become available. Setting
103 * this policy type immediately installs any pending updates that might be postponed or waiting
104 * for a maintenance window.
Rubin Xu8027a4f2015-03-10 17:52:37 +0000105 */
106 public static final int TYPE_INSTALL_AUTOMATIC = 1;
107
108 /**
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100109 * Installs system updates (without user interaction) during a daily maintenance window. Set the
110 * start and end of the daily maintenance window, as minutes of the day, when creating a new
111 * {@code TYPE_INSTALL_WINDOWED} policy. See
112 * {@link #createWindowedInstallPolicy createWindowedInstallPolicy()}.
Charles He57ed46a2017-01-03 11:48:07 +0000113 *
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100114 * <p>No connectivity, not enough disk space, or a low battery are typical reasons Android might
115 * not install a system update in the daily maintenance window. After 30 days trying to install
116 * an update in the maintenance window (regardless of policy changes in this period), the system
117 * prompts the device user to install the update.
Rubin Xu8027a4f2015-03-10 17:52:37 +0000118 */
119 public static final int TYPE_INSTALL_WINDOWED = 2;
120
121 /**
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100122 * Postpones the installation of system updates for 30 days. After the 30-day period has ended,
123 * the system prompts the device user to install the update.
Rubin Xu59af7a82017-04-24 15:11:43 +0100124 *
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100125 * <p>The system limits each update to one 30-day postponement. The period begins when the
126 * system first postpones the update and setting new {@code TYPE_POSTPONE} policies won’t extend
127 * the period. If, after 30 days the update isn’t installed (through policy changes), the system
128 * prompts the user to install the update.
Charles He57ed46a2017-01-03 11:48:07 +0000129 *
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100130 * <p><strong>Note</strong>: Device manufacturers or carriers might choose to exempt important
131 * security updates from a postponement policy. Exempted updates notify the device user when
132 * they become available.
Rubin Xu8027a4f2015-03-10 17:52:37 +0000133 */
134 public static final int TYPE_POSTPONE = 3;
135
Rubin Xu658d7bb2018-02-07 14:51:43 +0000136 /**
137 * Incoming system updates (including security updates) should be blocked. This flag is not
138 * exposed to third-party apps (and any attempt to set it will raise exceptions). This is used
139 * to represent the current installation option type to the privileged system update clients,
140 * for example to indicate OTA freeze is currently in place or when system is outside a daily
141 * maintenance window.
142 *
143 * @see InstallationOption
144 * @hide
145 */
146 @SystemApi
147 public static final int TYPE_PAUSE = 4;
148
Rubin Xu8027a4f2015-03-10 17:52:37 +0000149 private static final String KEY_POLICY_TYPE = "policy_type";
150 private static final String KEY_INSTALL_WINDOW_START = "install_window_start";
151 private static final String KEY_INSTALL_WINDOW_END = "install_window_end";
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000152 private static final String KEY_FREEZE_TAG = "freeze";
153 private static final String KEY_FREEZE_START = "start";
154 private static final String KEY_FREEZE_END = "end";
155
Rubin Xu8027a4f2015-03-10 17:52:37 +0000156 /**
Rubin Xud86d58c2015-05-05 16:57:37 +0100157 * The upper boundary of the daily maintenance window: 24 * 60 minutes.
Rubin Xu8027a4f2015-03-10 17:52:37 +0000158 */
Rubin Xud86d58c2015-05-05 16:57:37 +0100159 private static final int WINDOW_BOUNDARY = 24 * 60;
160
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000161 /**
162 * The maximum length of a single freeze period: 90 days.
163 */
164 static final int FREEZE_PERIOD_MAX_LENGTH = 90;
165
166 /**
167 * The minimum allowed time between two adjacent freeze period (from the end of the first
168 * freeze period to the start of the second freeze period, both exclusive): 60 days.
169 */
170 static final int FREEZE_PERIOD_MIN_SEPARATION = 60;
171
172
173 /**
174 * An exception class that represents various validation errors thrown from
175 * {@link SystemUpdatePolicy#setFreezePeriods} and
176 * {@link DevicePolicyManager#setSystemUpdatePolicy}
177 */
178 public static final class ValidationFailedException extends IllegalArgumentException
179 implements Parcelable {
180
181 /** @hide */
182 @IntDef(prefix = { "ERROR_" }, value = {
183 ERROR_NONE,
184 ERROR_DUPLICATE_OR_OVERLAP,
185 ERROR_NEW_FREEZE_PERIOD_TOO_LONG,
186 ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE,
187 ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG,
188 ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE,
Rubin Xu1b2f3742018-03-28 14:54:08 +0100189 ERROR_UNKNOWN,
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000190 })
191 @Retention(RetentionPolicy.SOURCE)
192 @interface ValidationFailureType {}
193
194 /** @hide */
195 public static final int ERROR_NONE = 0;
196
197 /**
Rubin Xu1b2f3742018-03-28 14:54:08 +0100198 * Validation failed with unknown error.
199 */
200 public static final int ERROR_UNKNOWN = 1;
201
202 /**
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000203 * The freeze periods contains duplicates, periods that overlap with each
204 * other or periods whose start and end joins.
205 */
Rubin Xu1b2f3742018-03-28 14:54:08 +0100206 public static final int ERROR_DUPLICATE_OR_OVERLAP = 2;
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000207
208 /**
209 * There exists at least one freeze period whose length exceeds 90 days.
210 */
Rubin Xu1b2f3742018-03-28 14:54:08 +0100211 public static final int ERROR_NEW_FREEZE_PERIOD_TOO_LONG = 3;
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000212
213 /**
214 * There exists some freeze period which starts within 60 days of the preceding period's
215 * end time.
216 */
Rubin Xu1b2f3742018-03-28 14:54:08 +0100217 public static final int ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE = 4;
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000218
219 /**
220 * The device has been in a freeze period and when combining with the new freeze period
221 * to be set, it will result in the total freeze period being longer than 90 days.
222 */
Rubin Xu1b2f3742018-03-28 14:54:08 +0100223 public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG = 5;
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000224
225 /**
226 * The device has been in a freeze period and some new freeze period to be set is less
227 * than 60 days from the end of the last freeze period the device went through.
228 */
Rubin Xu1b2f3742018-03-28 14:54:08 +0100229 public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE = 6;
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000230
231 @ValidationFailureType
232 private final int mErrorCode;
233
234 private ValidationFailedException(int errorCode, String message) {
235 super(message);
236 mErrorCode = errorCode;
237 }
238
239 /**
240 * Returns the type of validation error associated with this exception.
241 */
242 public @ValidationFailureType int getErrorCode() {
243 return mErrorCode;
244 }
245
246 /** @hide */
247 public static ValidationFailedException duplicateOrOverlapPeriods() {
248 return new ValidationFailedException(ERROR_DUPLICATE_OR_OVERLAP,
249 "Found duplicate or overlapping periods");
250 }
251
252 /** @hide */
253 public static ValidationFailedException freezePeriodTooLong(String message) {
254 return new ValidationFailedException(ERROR_NEW_FREEZE_PERIOD_TOO_LONG, message);
255 }
256
257 /** @hide */
258 public static ValidationFailedException freezePeriodTooClose(String message) {
259 return new ValidationFailedException(ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE, message);
260 }
261
262 /** @hide */
263 public static ValidationFailedException combinedPeriodTooLong(String message) {
264 return new ValidationFailedException(ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG, message);
265 }
266
267 /** @hide */
268 public static ValidationFailedException combinedPeriodTooClose(String message) {
269 return new ValidationFailedException(ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE, message);
270 }
271
272 @Override
273 public int describeContents() {
274 return 0;
275 }
276
277 @Override
278 public void writeToParcel(Parcel dest, int flags) {
279 dest.writeInt(mErrorCode);
280 dest.writeString(getMessage());
281 }
282
283 public static final Parcelable.Creator<ValidationFailedException> CREATOR =
284 new Parcelable.Creator<ValidationFailedException>() {
285 @Override
286 public ValidationFailedException createFromParcel(Parcel source) {
287 return new ValidationFailedException(source.readInt(), source.readString());
288 }
289
290 @Override
291 public ValidationFailedException[] newArray(int size) {
292 return new ValidationFailedException[size];
293 }
294
295 };
296 }
297
Rubin Xud86d58c2015-05-05 16:57:37 +0100298 @SystemUpdatePolicyType
299 private int mPolicyType;
300
301 private int mMaintenanceWindowStart;
302 private int mMaintenanceWindowEnd;
303
Rubin Xu1b2f3742018-03-28 14:54:08 +0100304 private final ArrayList<FreezePeriod> mFreezePeriods;
Rubin Xud86d58c2015-05-05 16:57:37 +0100305
306 private SystemUpdatePolicy() {
307 mPolicyType = TYPE_UNKNOWN;
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000308 mFreezePeriods = new ArrayList<>();
Rubin Xu8027a4f2015-03-10 17:52:37 +0000309 }
310
311 /**
Rubin Xud86d58c2015-05-05 16:57:37 +0100312 * Create a policy object and set it to install update automatically as soon as one is
313 * available.
Rubin Xu5faad8e2015-04-20 17:43:48 +0100314 *
315 * @see #TYPE_INSTALL_AUTOMATIC
Rubin Xu8027a4f2015-03-10 17:52:37 +0000316 */
Rubin Xud86d58c2015-05-05 16:57:37 +0100317 public static SystemUpdatePolicy createAutomaticInstallPolicy() {
318 SystemUpdatePolicy policy = new SystemUpdatePolicy();
319 policy.mPolicyType = TYPE_INSTALL_AUTOMATIC;
320 return policy;
Rubin Xu8027a4f2015-03-10 17:52:37 +0000321 }
322
323 /**
Rubin Xud86d58c2015-05-05 16:57:37 +0100324 * Create a policy object and set it to: new system update will only be installed automatically
325 * when the system clock is inside a daily maintenance window. If the start and end times are
Charles He57ed46a2017-01-03 11:48:07 +0000326 * the same, the window is considered to include the <i>whole 24 hours</i>. That is, updates can
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100327 * install at any time. If start time is later than end time, the window is considered spanning
Charles He57ed46a2017-01-03 11:48:07 +0000328 * midnight (i.e. the end time denotes a time on the next day). The maintenance window will last
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100329 * for 30 days for any given update, after which the window will no longer be effective and
330 * the pending update will be made available for manual installation as if no system update
331 * policy were set on the device. See {@link #TYPE_INSTALL_WINDOWED} for the details of this
332 * policy's behavior.
Rubin Xu8027a4f2015-03-10 17:52:37 +0000333 *
334 * @param startTime the start of the maintenance window, measured as the number of minutes from
Rubin Xu5faad8e2015-04-20 17:43:48 +0100335 * midnight in the device's local time. Must be in the range of [0, 1440).
Rubin Xu8027a4f2015-03-10 17:52:37 +0000336 * @param endTime the end of the maintenance window, measured as the number of minutes from
Rubin Xu5faad8e2015-04-20 17:43:48 +0100337 * midnight in the device's local time. Must be in the range of [0, 1440).
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100338 * @throws IllegalArgumentException If the {@code startTime} or {@code endTime} isn't in the
339 * accepted range.
340 * @return The configured policy.
Rubin Xu5faad8e2015-04-20 17:43:48 +0100341 * @see #TYPE_INSTALL_WINDOWED
Rubin Xu8027a4f2015-03-10 17:52:37 +0000342 */
Rubin Xud86d58c2015-05-05 16:57:37 +0100343 public static SystemUpdatePolicy createWindowedInstallPolicy(int startTime, int endTime) {
344 if (startTime < 0 || startTime >= WINDOW_BOUNDARY
345 || endTime < 0 || endTime >= WINDOW_BOUNDARY) {
346 throw new IllegalArgumentException("startTime and endTime must be inside [0, 1440)");
Rubin Xu8027a4f2015-03-10 17:52:37 +0000347 }
Rubin Xud86d58c2015-05-05 16:57:37 +0100348 SystemUpdatePolicy policy = new SystemUpdatePolicy();
349 policy.mPolicyType = TYPE_INSTALL_WINDOWED;
350 policy.mMaintenanceWindowStart = startTime;
351 policy.mMaintenanceWindowEnd = endTime;
352 return policy;
Rubin Xu8027a4f2015-03-10 17:52:37 +0000353 }
354
355 /**
Rubin Xud86d58c2015-05-05 16:57:37 +0100356 * Create a policy object and set it to block installation for a maximum period of 30 days.
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100357 * To learn more about this policy's behavior, see {@link #TYPE_POSTPONE}.
Rubin Xu5faad8e2015-04-20 17:43:48 +0100358 *
Charles He57ed46a2017-01-03 11:48:07 +0000359 * <p><b>Note: </b> security updates (e.g. monthly security patches) will <i>not</i> be affected
360 * by this policy.
361 *
Rubin Xu5faad8e2015-04-20 17:43:48 +0100362 * @see #TYPE_POSTPONE
Rubin Xu8027a4f2015-03-10 17:52:37 +0000363 */
Rubin Xud86d58c2015-05-05 16:57:37 +0100364 public static SystemUpdatePolicy createPostponeInstallPolicy() {
365 SystemUpdatePolicy policy = new SystemUpdatePolicy();
366 policy.mPolicyType = TYPE_POSTPONE;
367 return policy;
Rubin Xu8027a4f2015-03-10 17:52:37 +0000368 }
369
370 /**
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100371 * Returns the type of system update policy, or -1 if no policy has been set.
Rubin Xu8027a4f2015-03-10 17:52:37 +0000372 *
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100373 @return The policy type or -1 if the type isn't set.
Rubin Xu8027a4f2015-03-10 17:52:37 +0000374 */
Rubin Xu5faad8e2015-04-20 17:43:48 +0100375 @SystemUpdatePolicyType
Rubin Xu8027a4f2015-03-10 17:52:37 +0000376 public int getPolicyType() {
Rubin Xud86d58c2015-05-05 16:57:37 +0100377 return mPolicyType;
Rubin Xu8027a4f2015-03-10 17:52:37 +0000378 }
379
380 /**
381 * Get the start of the maintenance window.
382 *
383 * @return the start of the maintenance window measured as the number of minutes from midnight,
384 * or -1 if the policy does not have a maintenance window.
385 */
386 public int getInstallWindowStart() {
Rubin Xud86d58c2015-05-05 16:57:37 +0100387 if (mPolicyType == TYPE_INSTALL_WINDOWED) {
388 return mMaintenanceWindowStart;
Rubin Xu8027a4f2015-03-10 17:52:37 +0000389 } else {
390 return -1;
391 }
392 }
393
394 /**
395 * Get the end of the maintenance window.
396 *
397 * @return the end of the maintenance window measured as the number of minutes from midnight,
398 * or -1 if the policy does not have a maintenance window.
399 */
400 public int getInstallWindowEnd() {
Rubin Xud86d58c2015-05-05 16:57:37 +0100401 if (mPolicyType == TYPE_INSTALL_WINDOWED) {
402 return mMaintenanceWindowEnd;
Rubin Xu8027a4f2015-03-10 17:52:37 +0000403 } else {
404 return -1;
405 }
406 }
407
Rubin Xud86d58c2015-05-05 16:57:37 +0100408 /**
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000409 * Return if this object represents a valid policy with:
410 * 1. Correct type
411 * 2. Valid maintenance window if applicable
412 * 3. Valid freeze periods
Rubin Xud86d58c2015-05-05 16:57:37 +0100413 * @hide
414 */
415 public boolean isValid() {
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000416 try {
417 validateType();
418 validateFreezePeriods();
Rubin Xud86d58c2015-05-05 16:57:37 +0100419 return true;
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000420 } catch (IllegalArgumentException e) {
Rubin Xud86d58c2015-05-05 16:57:37 +0100421 return false;
422 }
423 }
424
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000425 /**
426 * Validate the type and maintenance window (if applicable) of this policy object,
427 * throws {@link IllegalArgumentException} if it's invalid.
428 * @hide
429 */
430 public void validateType() {
431 if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) {
432 return;
433 } else if (mPolicyType == TYPE_INSTALL_WINDOWED) {
434 if (!(mMaintenanceWindowStart >= 0 && mMaintenanceWindowStart < WINDOW_BOUNDARY
435 && mMaintenanceWindowEnd >= 0 && mMaintenanceWindowEnd < WINDOW_BOUNDARY)) {
436 throw new IllegalArgumentException("Invalid maintenance window");
437 }
438 } else {
439 throw new IllegalArgumentException("Invalid system update policy type.");
440 }
441 }
442
443 /**
444 * Configure a list of freeze periods on top of the current policy. When the device's clock is
445 * within any of the freeze periods, all incoming system updates including security patches will
446 * be blocked and cannot be installed. When the device is outside the freeze periods, the normal
447 * policy behavior will apply.
448 * <p>
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000449 * Each individual freeze period is allowed to be at most 90 days long, and adjacent freeze
450 * periods need to be at least 60 days apart. Also, the list of freeze periods should not
451 * contain duplicates or overlap with each other. If any of these conditions is not met, a
452 * {@link ValidationFailedException} will be thrown.
453 * <p>
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100454 * Handling of leap year: we ignore leap years in freeze period calculations, in particular,
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000455 * <ul>
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100456 * <li>When a freeze period is defined, February 29th is disregarded so even though a freeze
457 * period can be specified to start or end on February 29th, it will be treated as if the period
458 * started or ended on February 28th.</li>
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000459 * <li>When applying freeze period behavior to the device, a system clock of February 29th is
460 * treated as if it were February 28th</li>
461 * <li>When calculating the number of days of a freeze period or separation between two freeze
462 * periods, February 29th is also ignored and not counted as one day.</li>
463 * </ul>
464 *
465 * @param freezePeriods the list of freeze periods
466 * @throws ValidationFailedException if the supplied freeze periods do not meet the
467 * requirement set above
468 * @return this instance
469 */
Rubin Xu1b2f3742018-03-28 14:54:08 +0100470 public SystemUpdatePolicy setFreezePeriods(List<FreezePeriod> freezePeriods) {
471 FreezePeriod.validatePeriods(freezePeriods);
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000472 mFreezePeriods.clear();
Rubin Xu1b2f3742018-03-28 14:54:08 +0100473 mFreezePeriods.addAll(freezePeriods);
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000474 return this;
475 }
476
477 /**
478 * Returns the list of freeze periods previously set on this system update policy object.
479 *
480 * @return the list of freeze periods, or an empty list if none was set.
481 */
Rubin Xu1b2f3742018-03-28 14:54:08 +0100482 public List<FreezePeriod> getFreezePeriods() {
483 return Collections.unmodifiableList(mFreezePeriods);
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000484 }
485
486 /**
487 * Returns the real calendar dates of the current freeze period, or null if the device
488 * is not in a freeze period at the moment.
489 * @hide
490 */
491 public Pair<LocalDate, LocalDate> getCurrentFreezePeriod(LocalDate now) {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100492 for (FreezePeriod interval : mFreezePeriods) {
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000493 if (interval.contains(now)) {
494 return interval.toCurrentOrFutureRealDates(now);
495 }
496 }
497 return null;
498 }
499
Rubin Xu658d7bb2018-02-07 14:51:43 +0000500 /**
501 * Returns time (in milliseconds) until the start of the next freeze period, assuming now
502 * is not within a freeze period.
503 */
504 private long timeUntilNextFreezePeriod(long now) {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100505 List<FreezePeriod> sortedPeriods = FreezePeriod.canonicalizePeriods(mFreezePeriods);
Rubin Xu658d7bb2018-02-07 14:51:43 +0000506 LocalDate nowDate = millisToDate(now);
507 LocalDate nextFreezeStart = null;
Rubin Xu1b2f3742018-03-28 14:54:08 +0100508 for (FreezePeriod interval : sortedPeriods) {
Rubin Xu658d7bb2018-02-07 14:51:43 +0000509 if (interval.after(nowDate)) {
510 nextFreezeStart = interval.toCurrentOrFutureRealDates(nowDate).first;
511 break;
512 } else if (interval.contains(nowDate)) {
513 throw new IllegalArgumentException("Given date is inside a freeze period");
514 }
515 }
516 if (nextFreezeStart == null) {
517 // If no interval is after now, then it must be the one that starts at the beginning
518 // of next year
519 nextFreezeStart = sortedPeriods.get(0).toCurrentOrFutureRealDates(nowDate).first;
520 }
521 return dateToMillis(nextFreezeStart) - now;
522 }
523
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000524 /** @hide */
525 public void validateFreezePeriods() {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100526 FreezePeriod.validatePeriods(mFreezePeriods);
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000527 }
528
529 /** @hide */
530 public void validateAgainstPreviousFreezePeriod(LocalDate prevPeriodStart,
531 LocalDate prevPeriodEnd, LocalDate now) {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100532 FreezePeriod.validateAgainstPreviousFreezePeriod(mFreezePeriods, prevPeriodStart,
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000533 prevPeriodEnd, now);
534 }
535
Rubin Xu658d7bb2018-02-07 14:51:43 +0000536 /**
537 * An installation option represents how system update clients should act on incoming system
538 * updates and how long this action is valid for, given the current system update policy. Its
539 * action could be one of the following
540 * <ul>
Rubin Xu1b2f3742018-03-28 14:54:08 +0100541 * <li> {@link #TYPE_INSTALL_AUTOMATIC} system updates should be installed immedately and
542 * without user intervention as soon as they become available.
543 * <li> {@link #TYPE_POSTPONE} system updates should be postponed for a maximum of 30 days
544 * <li> {@link #TYPE_PAUSE} system updates should be postponed indefinitely until further notice
Rubin Xu658d7bb2018-02-07 14:51:43 +0000545 * </ul>
546 *
547 * The effective time measures how long this installation option is valid for from the queried
548 * time, in milliseconds.
549 *
550 * This is an internal API for system update clients.
551 * @hide
552 */
553 @SystemApi
554 public static class InstallationOption {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100555 /** @hide */
556 @IntDef(prefix = { "TYPE_" }, value = {
557 TYPE_INSTALL_AUTOMATIC,
558 TYPE_PAUSE,
559 TYPE_POSTPONE
560 })
561 @Retention(RetentionPolicy.SOURCE)
562 @interface InstallationOptionType {}
563
564 @InstallationOptionType
Rubin Xu658d7bb2018-02-07 14:51:43 +0000565 private final int mType;
566 private long mEffectiveTime;
567
Rubin Xu1b2f3742018-03-28 14:54:08 +0100568 InstallationOption(@InstallationOptionType int type, long effectiveTime) {
Rubin Xu658d7bb2018-02-07 14:51:43 +0000569 this.mType = type;
570 this.mEffectiveTime = effectiveTime;
571 }
572
Rubin Xu1b2f3742018-03-28 14:54:08 +0100573 /**
574 * Returns the type of the current installation option, could be one of
575 * {@link #TYPE_INSTALL_AUTOMATIC}, {@link #TYPE_POSTPONE} and {@link #TYPE_PAUSE}.
576 * @return type of installation option.
577 */
578 public @InstallationOptionType int getType() {
Rubin Xu658d7bb2018-02-07 14:51:43 +0000579 return mType;
580 }
581
Rubin Xu1b2f3742018-03-28 14:54:08 +0100582 /**
583 * Returns how long the current installation option in effective for, starting from the time
584 * of query.
585 * @return the effective time in milliseconds.
586 */
Rubin Xu658d7bb2018-02-07 14:51:43 +0000587 public long getEffectiveTime() {
588 return mEffectiveTime;
589 }
590
591 /** @hide */
592 protected void limitEffectiveTime(long otherTime) {
593 mEffectiveTime = Long.min(mEffectiveTime, otherTime);
594 }
595 }
596
597 /**
598 * Returns the installation option at the specified time, under the current
599 * {@code SystemUpdatePolicy} object. This is a convenience method for system update clients
600 * so they can instantiate this policy at any given time and find out what to do with incoming
601 * system updates, without the need of examining the overall policy structure.
602 *
603 * Normally the system update clients will query the current installation option by calling this
604 * method with the current timestamp, and act on the returned option until its effective time
605 * lapses. It can then query the latest option using a new timestamp. It should also listen
606 * for {@code DevicePolicyManager#ACTION_SYSTEM_UPDATE_POLICY_CHANGED} broadcast, in case the
607 * whole policy is updated.
608 *
609 * @param when At what time the intallation option is being queried, specified in number of
610 milliseonds since the epoch.
611 * @see InstallationOption
612 * @hide
613 */
614 @SystemApi
615 public InstallationOption getInstallationOptionAt(long when) {
616 LocalDate whenDate = millisToDate(when);
617 Pair<LocalDate, LocalDate> current = getCurrentFreezePeriod(whenDate);
618 if (current != null) {
619 return new InstallationOption(TYPE_PAUSE,
620 dateToMillis(roundUpLeapDay(current.second).plusDays(1)) - when);
621 }
622 // We are not within a freeze period, query the underlying policy.
623 // But also consider the start of the next freeze period, which might
624 // reduce the effective time of the current installation option
625 InstallationOption option = getInstallationOptionRegardlessFreezeAt(when);
626 if (mFreezePeriods.size() > 0) {
627 option.limitEffectiveTime(timeUntilNextFreezePeriod(when));
628 }
629 return option;
630 }
631
632 private InstallationOption getInstallationOptionRegardlessFreezeAt(long when) {
633 if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) {
634 return new InstallationOption(mPolicyType, Long.MAX_VALUE);
635 } else if (mPolicyType == TYPE_INSTALL_WINDOWED) {
636 Calendar query = Calendar.getInstance();
637 query.setTimeInMillis(when);
638 // Calculate the number of milliseconds since midnight of the time specified by when
639 long whenMillis = TimeUnit.HOURS.toMillis(query.get(Calendar.HOUR_OF_DAY))
640 + TimeUnit.MINUTES.toMillis(query.get(Calendar.MINUTE))
641 + TimeUnit.SECONDS.toMillis(query.get(Calendar.SECOND))
642 + query.get(Calendar.MILLISECOND);
643 long windowStartMillis = TimeUnit.MINUTES.toMillis(mMaintenanceWindowStart);
644 long windowEndMillis = TimeUnit.MINUTES.toMillis(mMaintenanceWindowEnd);
645 final long dayInMillis = TimeUnit.DAYS.toMillis(1);
646
647 if ((windowStartMillis <= whenMillis && whenMillis <= windowEndMillis)
648 || ((windowStartMillis > windowEndMillis)
649 && (windowStartMillis <= whenMillis || whenMillis <= windowEndMillis))) {
650 return new InstallationOption(TYPE_INSTALL_AUTOMATIC,
651 (windowEndMillis - whenMillis + dayInMillis) % dayInMillis);
652 } else {
653 return new InstallationOption(TYPE_PAUSE,
654 (windowStartMillis - whenMillis + dayInMillis) % dayInMillis);
655 }
656 } else {
657 throw new RuntimeException("Unknown policy type");
658 }
659 }
660
661 private static LocalDate roundUpLeapDay(LocalDate date) {
662 if (date.isLeapYear() && date.getMonthValue() == 2 && date.getDayOfMonth() == 28) {
663 return date.plusDays(1);
664 } else {
665 return date;
666 }
667 }
668
669 /** Convert a timestamp since epoch to a LocalDate using default timezone, truncating
670 * the hour/min/seconds part.
671 */
672 private static LocalDate millisToDate(long when) {
673 return Instant.ofEpochMilli(when).atZone(ZoneId.systemDefault()).toLocalDate();
674 }
675
676 /**
677 * Returns the timestamp since epoch of a LocalDate, assuming the time is 00:00:00.
678 */
679 private static long dateToMillis(LocalDate when) {
680 return LocalDateTime.of(when, LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant()
681 .toEpochMilli();
682 }
683
Rubin Xu8027a4f2015-03-10 17:52:37 +0000684 @Override
685 public String toString() {
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000686 return String.format("SystemUpdatePolicy (type: %d, windowStart: %d, windowEnd: %d, "
687 + "freezes: [%s])",
688 mPolicyType, mMaintenanceWindowStart, mMaintenanceWindowEnd,
689 mFreezePeriods.stream().map(n -> n.toString()).collect(Collectors.joining(",")));
Rubin Xud86d58c2015-05-05 16:57:37 +0100690 }
691
692 @Override
693 public int describeContents() {
694 return 0;
695 }
696
697 @Override
698 public void writeToParcel(Parcel dest, int flags) {
699 dest.writeInt(mPolicyType);
700 dest.writeInt(mMaintenanceWindowStart);
701 dest.writeInt(mMaintenanceWindowEnd);
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000702 int freezeCount = mFreezePeriods.size();
703 dest.writeInt(freezeCount);
704 for (int i = 0; i < freezeCount; i++) {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100705 FreezePeriod interval = mFreezePeriods.get(i);
706 dest.writeInt(interval.getStart().getMonthValue());
707 dest.writeInt(interval.getStart().getDayOfMonth());
708 dest.writeInt(interval.getEnd().getMonthValue());
709 dest.writeInt(interval.getEnd().getDayOfMonth());
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000710 }
Rubin Xud86d58c2015-05-05 16:57:37 +0100711 }
712
713 public static final Parcelable.Creator<SystemUpdatePolicy> CREATOR =
714 new Parcelable.Creator<SystemUpdatePolicy>() {
715
716 @Override
717 public SystemUpdatePolicy createFromParcel(Parcel source) {
718 SystemUpdatePolicy policy = new SystemUpdatePolicy();
719 policy.mPolicyType = source.readInt();
720 policy.mMaintenanceWindowStart = source.readInt();
721 policy.mMaintenanceWindowEnd = source.readInt();
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000722 int freezeCount = source.readInt();
723 policy.mFreezePeriods.ensureCapacity(freezeCount);
724 for (int i = 0; i < freezeCount; i++) {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100725 MonthDay start = MonthDay.of(source.readInt(), source.readInt());
726 MonthDay end = MonthDay.of(source.readInt(), source.readInt());
727 policy.mFreezePeriods.add(new FreezePeriod(start, end));
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000728 }
Rubin Xud86d58c2015-05-05 16:57:37 +0100729 return policy;
730 }
731
732 @Override
733 public SystemUpdatePolicy[] newArray(int size) {
734 return new SystemUpdatePolicy[size];
735 }
736 };
737
Rubin Xud86d58c2015-05-05 16:57:37 +0100738 /**
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000739 * Restore a previously saved SystemUpdatePolicy from XML. No need to validate
740 * the reconstructed policy since the XML is supposed to be created by the
741 * system server from a validated policy object previously.
Rubin Xud86d58c2015-05-05 16:57:37 +0100742 * @hide
743 */
744 public static SystemUpdatePolicy restoreFromXml(XmlPullParser parser) {
745 try {
746 SystemUpdatePolicy policy = new SystemUpdatePolicy();
747 String value = parser.getAttributeValue(null, KEY_POLICY_TYPE);
748 if (value != null) {
749 policy.mPolicyType = Integer.parseInt(value);
750
751 value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_START);
752 if (value != null) {
753 policy.mMaintenanceWindowStart = Integer.parseInt(value);
754 }
755 value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_END);
756 if (value != null) {
757 policy.mMaintenanceWindowEnd = Integer.parseInt(value);
758 }
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000759
760 int outerDepth = parser.getDepth();
761 int type;
762 while ((type = parser.next()) != END_DOCUMENT
763 && (type != END_TAG || parser.getDepth() > outerDepth)) {
764 if (type == END_TAG || type == TEXT) {
765 continue;
766 }
767 if (!parser.getName().equals(KEY_FREEZE_TAG)) {
768 continue;
769 }
Rubin Xu1b2f3742018-03-28 14:54:08 +0100770 policy.mFreezePeriods.add(new FreezePeriod(
771 MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_START)),
772 MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_END))));
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000773 }
Rubin Xud86d58c2015-05-05 16:57:37 +0100774 return policy;
775 }
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000776 } catch (NumberFormatException | XmlPullParserException | IOException e) {
Rubin Xud86d58c2015-05-05 16:57:37 +0100777 // Fail through
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000778 Log.w(TAG, "Load xml failed", e);
Rubin Xud86d58c2015-05-05 16:57:37 +0100779 }
780 return null;
Rubin Xu8027a4f2015-03-10 17:52:37 +0000781 }
782
783 /**
Rubin Xud86d58c2015-05-05 16:57:37 +0100784 * @hide
Rubin Xu8027a4f2015-03-10 17:52:37 +0000785 */
Rubin Xud86d58c2015-05-05 16:57:37 +0100786 public void saveToXml(XmlSerializer out) throws IOException {
787 out.attribute(null, KEY_POLICY_TYPE, Integer.toString(mPolicyType));
788 out.attribute(null, KEY_INSTALL_WINDOW_START, Integer.toString(mMaintenanceWindowStart));
789 out.attribute(null, KEY_INSTALL_WINDOW_END, Integer.toString(mMaintenanceWindowEnd));
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000790 for (int i = 0; i < mFreezePeriods.size(); i++) {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100791 FreezePeriod interval = mFreezePeriods.get(i);
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000792 out.startTag(null, KEY_FREEZE_TAG);
Rubin Xu1b2f3742018-03-28 14:54:08 +0100793 out.attribute(null, KEY_FREEZE_START, interval.getStart().toString());
794 out.attribute(null, KEY_FREEZE_END, interval.getEnd().toString());
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000795 out.endTag(null, KEY_FREEZE_TAG);
796 }
Rubin Xu8027a4f2015-03-10 17:52:37 +0000797 }
798}
799