blob: 96dbc760278ff733a3be6a4c0adee2041c8b4da2 [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>
79 * To learn more about managing system updates, read
80 * <a href="{@docRoot}/work/dpc/security.html#control_remote_software_updates">Control remote
81 * software updates</a>.
Rubin Xu8027a4f2015-03-10 17:52:37 +000082 *
Rubin Xu5faad8e2015-04-20 17:43:48 +010083 * @see DevicePolicyManager#setSystemUpdatePolicy
84 * @see DevicePolicyManager#getSystemUpdatePolicy
Rubin Xu8027a4f2015-03-10 17:52:37 +000085 */
Rubin Xu1b2f3742018-03-28 14:54:08 +010086public final class SystemUpdatePolicy implements Parcelable {
Rubin Xu29b9a7d2018-01-11 09:24:02 +000087 private static final String TAG = "SystemUpdatePolicy";
Rubin Xu8027a4f2015-03-10 17:52:37 +000088
89 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -070090 @IntDef(prefix = { "TYPE_" }, value = {
91 TYPE_INSTALL_AUTOMATIC,
92 TYPE_INSTALL_WINDOWED,
93 TYPE_POSTPONE
94 })
Rubin Xu8027a4f2015-03-10 17:52:37 +000095 @Retention(RetentionPolicy.SOURCE)
Rubin Xu5faad8e2015-04-20 17:43:48 +010096 @interface SystemUpdatePolicyType {}
Rubin Xu8027a4f2015-03-10 17:52:37 +000097
98 /**
Rubin Xud86d58c2015-05-05 16:57:37 +010099 * Unknown policy type, used only internally.
100 */
101 private static final int TYPE_UNKNOWN = -1;
Charles He57ed46a2017-01-03 11:48:07 +0000102
Rubin Xud86d58c2015-05-05 16:57:37 +0100103 /**
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100104 * Installs system updates (without user interaction) as soon as they become available. Setting
105 * this policy type immediately installs any pending updates that might be postponed or waiting
106 * for a maintenance window.
Rubin Xu8027a4f2015-03-10 17:52:37 +0000107 */
108 public static final int TYPE_INSTALL_AUTOMATIC = 1;
109
110 /**
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100111 * Installs system updates (without user interaction) during a daily maintenance window. Set the
112 * start and end of the daily maintenance window, as minutes of the day, when creating a new
113 * {@code TYPE_INSTALL_WINDOWED} policy. See
114 * {@link #createWindowedInstallPolicy createWindowedInstallPolicy()}.
Charles He57ed46a2017-01-03 11:48:07 +0000115 *
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100116 * <p>No connectivity, not enough disk space, or a low battery are typical reasons Android might
117 * not install a system update in the daily maintenance window. After 30 days trying to install
118 * an update in the maintenance window (regardless of policy changes in this period), the system
119 * prompts the device user to install the update.
Rubin Xu8027a4f2015-03-10 17:52:37 +0000120 */
121 public static final int TYPE_INSTALL_WINDOWED = 2;
122
123 /**
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100124 * Postpones the installation of system updates for 30 days. After the 30-day period has ended,
125 * the system prompts the device user to install the update.
Rubin Xu59af7a82017-04-24 15:11:43 +0100126 *
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100127 * <p>The system limits each update to one 30-day postponement. The period begins when the
128 * system first postpones the update and setting new {@code TYPE_POSTPONE} policies won’t extend
129 * the period. If, after 30 days the update isn’t installed (through policy changes), the system
130 * prompts the user to install the update.
Charles He57ed46a2017-01-03 11:48:07 +0000131 *
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100132 * <p><strong>Note</strong>: Device manufacturers or carriers might choose to exempt important
133 * security updates from a postponement policy. Exempted updates notify the device user when
134 * they become available.
Rubin Xu8027a4f2015-03-10 17:52:37 +0000135 */
136 public static final int TYPE_POSTPONE = 3;
137
Rubin Xu658d7bb2018-02-07 14:51:43 +0000138 /**
139 * Incoming system updates (including security updates) should be blocked. This flag is not
140 * exposed to third-party apps (and any attempt to set it will raise exceptions). This is used
141 * to represent the current installation option type to the privileged system update clients,
142 * for example to indicate OTA freeze is currently in place or when system is outside a daily
143 * maintenance window.
144 *
145 * @see InstallationOption
146 * @hide
147 */
148 @SystemApi
149 public static final int TYPE_PAUSE = 4;
150
Rubin Xu8027a4f2015-03-10 17:52:37 +0000151 private static final String KEY_POLICY_TYPE = "policy_type";
152 private static final String KEY_INSTALL_WINDOW_START = "install_window_start";
153 private static final String KEY_INSTALL_WINDOW_END = "install_window_end";
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000154 private static final String KEY_FREEZE_TAG = "freeze";
155 private static final String KEY_FREEZE_START = "start";
156 private static final String KEY_FREEZE_END = "end";
157
Rubin Xu8027a4f2015-03-10 17:52:37 +0000158 /**
Rubin Xud86d58c2015-05-05 16:57:37 +0100159 * The upper boundary of the daily maintenance window: 24 * 60 minutes.
Rubin Xu8027a4f2015-03-10 17:52:37 +0000160 */
Rubin Xud86d58c2015-05-05 16:57:37 +0100161 private static final int WINDOW_BOUNDARY = 24 * 60;
162
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000163 /**
164 * The maximum length of a single freeze period: 90 days.
165 */
166 static final int FREEZE_PERIOD_MAX_LENGTH = 90;
167
168 /**
169 * The minimum allowed time between two adjacent freeze period (from the end of the first
170 * freeze period to the start of the second freeze period, both exclusive): 60 days.
171 */
172 static final int FREEZE_PERIOD_MIN_SEPARATION = 60;
173
174
175 /**
176 * An exception class that represents various validation errors thrown from
177 * {@link SystemUpdatePolicy#setFreezePeriods} and
178 * {@link DevicePolicyManager#setSystemUpdatePolicy}
179 */
180 public static final class ValidationFailedException extends IllegalArgumentException
181 implements Parcelable {
182
183 /** @hide */
184 @IntDef(prefix = { "ERROR_" }, value = {
185 ERROR_NONE,
186 ERROR_DUPLICATE_OR_OVERLAP,
187 ERROR_NEW_FREEZE_PERIOD_TOO_LONG,
188 ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE,
189 ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG,
190 ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE,
Rubin Xu1b2f3742018-03-28 14:54:08 +0100191 ERROR_UNKNOWN,
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000192 })
193 @Retention(RetentionPolicy.SOURCE)
194 @interface ValidationFailureType {}
195
196 /** @hide */
197 public static final int ERROR_NONE = 0;
198
199 /**
Rubin Xu1b2f3742018-03-28 14:54:08 +0100200 * Validation failed with unknown error.
201 */
202 public static final int ERROR_UNKNOWN = 1;
203
204 /**
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000205 * The freeze periods contains duplicates, periods that overlap with each
206 * other or periods whose start and end joins.
207 */
Rubin Xu1b2f3742018-03-28 14:54:08 +0100208 public static final int ERROR_DUPLICATE_OR_OVERLAP = 2;
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000209
210 /**
211 * There exists at least one freeze period whose length exceeds 90 days.
212 */
Rubin Xu1b2f3742018-03-28 14:54:08 +0100213 public static final int ERROR_NEW_FREEZE_PERIOD_TOO_LONG = 3;
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000214
215 /**
216 * There exists some freeze period which starts within 60 days of the preceding period's
217 * end time.
218 */
Rubin Xu1b2f3742018-03-28 14:54:08 +0100219 public static final int ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE = 4;
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000220
221 /**
222 * The device has been in a freeze period and when combining with the new freeze period
223 * to be set, it will result in the total freeze period being longer than 90 days.
224 */
Rubin Xu1b2f3742018-03-28 14:54:08 +0100225 public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG = 5;
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000226
227 /**
228 * The device has been in a freeze period and some new freeze period to be set is less
229 * than 60 days from the end of the last freeze period the device went through.
230 */
Rubin Xu1b2f3742018-03-28 14:54:08 +0100231 public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE = 6;
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000232
233 @ValidationFailureType
234 private final int mErrorCode;
235
236 private ValidationFailedException(int errorCode, String message) {
237 super(message);
238 mErrorCode = errorCode;
239 }
240
241 /**
242 * Returns the type of validation error associated with this exception.
243 */
244 public @ValidationFailureType int getErrorCode() {
245 return mErrorCode;
246 }
247
248 /** @hide */
249 public static ValidationFailedException duplicateOrOverlapPeriods() {
250 return new ValidationFailedException(ERROR_DUPLICATE_OR_OVERLAP,
251 "Found duplicate or overlapping periods");
252 }
253
254 /** @hide */
255 public static ValidationFailedException freezePeriodTooLong(String message) {
256 return new ValidationFailedException(ERROR_NEW_FREEZE_PERIOD_TOO_LONG, message);
257 }
258
259 /** @hide */
260 public static ValidationFailedException freezePeriodTooClose(String message) {
261 return new ValidationFailedException(ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE, message);
262 }
263
264 /** @hide */
265 public static ValidationFailedException combinedPeriodTooLong(String message) {
266 return new ValidationFailedException(ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG, message);
267 }
268
269 /** @hide */
270 public static ValidationFailedException combinedPeriodTooClose(String message) {
271 return new ValidationFailedException(ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE, message);
272 }
273
274 @Override
275 public int describeContents() {
276 return 0;
277 }
278
279 @Override
280 public void writeToParcel(Parcel dest, int flags) {
281 dest.writeInt(mErrorCode);
282 dest.writeString(getMessage());
283 }
284
285 public static final Parcelable.Creator<ValidationFailedException> CREATOR =
286 new Parcelable.Creator<ValidationFailedException>() {
287 @Override
288 public ValidationFailedException createFromParcel(Parcel source) {
289 return new ValidationFailedException(source.readInt(), source.readString());
290 }
291
292 @Override
293 public ValidationFailedException[] newArray(int size) {
294 return new ValidationFailedException[size];
295 }
296
297 };
298 }
299
Rubin Xud86d58c2015-05-05 16:57:37 +0100300 @SystemUpdatePolicyType
301 private int mPolicyType;
302
303 private int mMaintenanceWindowStart;
304 private int mMaintenanceWindowEnd;
305
Rubin Xu1b2f3742018-03-28 14:54:08 +0100306 private final ArrayList<FreezePeriod> mFreezePeriods;
Rubin Xud86d58c2015-05-05 16:57:37 +0100307
308 private SystemUpdatePolicy() {
309 mPolicyType = TYPE_UNKNOWN;
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000310 mFreezePeriods = new ArrayList<>();
Rubin Xu8027a4f2015-03-10 17:52:37 +0000311 }
312
313 /**
Rubin Xud86d58c2015-05-05 16:57:37 +0100314 * Create a policy object and set it to install update automatically as soon as one is
315 * available.
Rubin Xu5faad8e2015-04-20 17:43:48 +0100316 *
317 * @see #TYPE_INSTALL_AUTOMATIC
Rubin Xu8027a4f2015-03-10 17:52:37 +0000318 */
Rubin Xud86d58c2015-05-05 16:57:37 +0100319 public static SystemUpdatePolicy createAutomaticInstallPolicy() {
320 SystemUpdatePolicy policy = new SystemUpdatePolicy();
321 policy.mPolicyType = TYPE_INSTALL_AUTOMATIC;
322 return policy;
Rubin Xu8027a4f2015-03-10 17:52:37 +0000323 }
324
325 /**
Rubin Xud86d58c2015-05-05 16:57:37 +0100326 * Create a policy object and set it to: new system update will only be installed automatically
327 * when the system clock is inside a daily maintenance window. If the start and end times are
Charles He57ed46a2017-01-03 11:48:07 +0000328 * 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 +0100329 * install at any time. If start time is later than end time, the window is considered spanning
Charles He57ed46a2017-01-03 11:48:07 +0000330 * 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 +0100331 * for 30 days for any given update, after which the window will no longer be effective and
332 * the pending update will be made available for manual installation as if no system update
333 * policy were set on the device. See {@link #TYPE_INSTALL_WINDOWED} for the details of this
334 * policy's behavior.
Rubin Xu8027a4f2015-03-10 17:52:37 +0000335 *
336 * @param startTime the start 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 Xu8027a4f2015-03-10 17:52:37 +0000338 * @param endTime the end of the maintenance window, measured as the number of minutes from
Rubin Xu5faad8e2015-04-20 17:43:48 +0100339 * midnight in the device's local time. Must be in the range of [0, 1440).
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100340 * @throws IllegalArgumentException If the {@code startTime} or {@code endTime} isn't in the
341 * accepted range.
342 * @return The configured policy.
Rubin Xu5faad8e2015-04-20 17:43:48 +0100343 * @see #TYPE_INSTALL_WINDOWED
Rubin Xu8027a4f2015-03-10 17:52:37 +0000344 */
Rubin Xud86d58c2015-05-05 16:57:37 +0100345 public static SystemUpdatePolicy createWindowedInstallPolicy(int startTime, int endTime) {
346 if (startTime < 0 || startTime >= WINDOW_BOUNDARY
347 || endTime < 0 || endTime >= WINDOW_BOUNDARY) {
348 throw new IllegalArgumentException("startTime and endTime must be inside [0, 1440)");
Rubin Xu8027a4f2015-03-10 17:52:37 +0000349 }
Rubin Xud86d58c2015-05-05 16:57:37 +0100350 SystemUpdatePolicy policy = new SystemUpdatePolicy();
351 policy.mPolicyType = TYPE_INSTALL_WINDOWED;
352 policy.mMaintenanceWindowStart = startTime;
353 policy.mMaintenanceWindowEnd = endTime;
354 return policy;
Rubin Xu8027a4f2015-03-10 17:52:37 +0000355 }
356
357 /**
Rubin Xud86d58c2015-05-05 16:57:37 +0100358 * Create a policy object and set it to block installation for a maximum period of 30 days.
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100359 * To learn more about this policy's behavior, see {@link #TYPE_POSTPONE}.
Rubin Xu5faad8e2015-04-20 17:43:48 +0100360 *
Charles He57ed46a2017-01-03 11:48:07 +0000361 * <p><b>Note: </b> security updates (e.g. monthly security patches) will <i>not</i> be affected
362 * by this policy.
363 *
Rubin Xu5faad8e2015-04-20 17:43:48 +0100364 * @see #TYPE_POSTPONE
Rubin Xu8027a4f2015-03-10 17:52:37 +0000365 */
Rubin Xud86d58c2015-05-05 16:57:37 +0100366 public static SystemUpdatePolicy createPostponeInstallPolicy() {
367 SystemUpdatePolicy policy = new SystemUpdatePolicy();
368 policy.mPolicyType = TYPE_POSTPONE;
369 return policy;
Rubin Xu8027a4f2015-03-10 17:52:37 +0000370 }
371
372 /**
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100373 * Returns the type of system update policy, or -1 if no policy has been set.
Rubin Xu8027a4f2015-03-10 17:52:37 +0000374 *
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100375 @return The policy type or -1 if the type isn't set.
Rubin Xu8027a4f2015-03-10 17:52:37 +0000376 */
Rubin Xu5faad8e2015-04-20 17:43:48 +0100377 @SystemUpdatePolicyType
Rubin Xu8027a4f2015-03-10 17:52:37 +0000378 public int getPolicyType() {
Rubin Xud86d58c2015-05-05 16:57:37 +0100379 return mPolicyType;
Rubin Xu8027a4f2015-03-10 17:52:37 +0000380 }
381
382 /**
383 * Get the start of the maintenance window.
384 *
385 * @return the start of the maintenance window measured as the number of minutes from midnight,
386 * or -1 if the policy does not have a maintenance window.
387 */
388 public int getInstallWindowStart() {
Rubin Xud86d58c2015-05-05 16:57:37 +0100389 if (mPolicyType == TYPE_INSTALL_WINDOWED) {
390 return mMaintenanceWindowStart;
Rubin Xu8027a4f2015-03-10 17:52:37 +0000391 } else {
392 return -1;
393 }
394 }
395
396 /**
397 * Get the end of the maintenance window.
398 *
399 * @return the end of the maintenance window measured as the number of minutes from midnight,
400 * or -1 if the policy does not have a maintenance window.
401 */
402 public int getInstallWindowEnd() {
Rubin Xud86d58c2015-05-05 16:57:37 +0100403 if (mPolicyType == TYPE_INSTALL_WINDOWED) {
404 return mMaintenanceWindowEnd;
Rubin Xu8027a4f2015-03-10 17:52:37 +0000405 } else {
406 return -1;
407 }
408 }
409
Rubin Xud86d58c2015-05-05 16:57:37 +0100410 /**
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000411 * Return if this object represents a valid policy with:
412 * 1. Correct type
413 * 2. Valid maintenance window if applicable
414 * 3. Valid freeze periods
Rubin Xud86d58c2015-05-05 16:57:37 +0100415 * @hide
416 */
417 public boolean isValid() {
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000418 try {
419 validateType();
420 validateFreezePeriods();
Rubin Xud86d58c2015-05-05 16:57:37 +0100421 return true;
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000422 } catch (IllegalArgumentException e) {
Rubin Xud86d58c2015-05-05 16:57:37 +0100423 return false;
424 }
425 }
426
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000427 /**
428 * Validate the type and maintenance window (if applicable) of this policy object,
429 * throws {@link IllegalArgumentException} if it's invalid.
430 * @hide
431 */
432 public void validateType() {
433 if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) {
434 return;
435 } else if (mPolicyType == TYPE_INSTALL_WINDOWED) {
436 if (!(mMaintenanceWindowStart >= 0 && mMaintenanceWindowStart < WINDOW_BOUNDARY
437 && mMaintenanceWindowEnd >= 0 && mMaintenanceWindowEnd < WINDOW_BOUNDARY)) {
438 throw new IllegalArgumentException("Invalid maintenance window");
439 }
440 } else {
441 throw new IllegalArgumentException("Invalid system update policy type.");
442 }
443 }
444
445 /**
446 * Configure a list of freeze periods on top of the current policy. When the device's clock is
447 * within any of the freeze periods, all incoming system updates including security patches will
448 * be blocked and cannot be installed. When the device is outside the freeze periods, the normal
449 * policy behavior will apply.
450 * <p>
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000451 * Each individual freeze period is allowed to be at most 90 days long, and adjacent freeze
452 * periods need to be at least 60 days apart. Also, the list of freeze periods should not
453 * contain duplicates or overlap with each other. If any of these conditions is not met, a
454 * {@link ValidationFailedException} will be thrown.
455 * <p>
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100456 * Handling of leap year: we ignore leap years in freeze period calculations, in particular,
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000457 * <ul>
Rubin Xufcf3d6e2018-04-18 15:18:12 +0100458 * <li>When a freeze period is defined, February 29th is disregarded so even though a freeze
459 * period can be specified to start or end on February 29th, it will be treated as if the period
460 * started or ended on February 28th.</li>
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000461 * <li>When applying freeze period behavior to the device, a system clock of February 29th is
462 * treated as if it were February 28th</li>
463 * <li>When calculating the number of days of a freeze period or separation between two freeze
464 * periods, February 29th is also ignored and not counted as one day.</li>
465 * </ul>
466 *
467 * @param freezePeriods the list of freeze periods
468 * @throws ValidationFailedException if the supplied freeze periods do not meet the
469 * requirement set above
470 * @return this instance
471 */
Rubin Xu1b2f3742018-03-28 14:54:08 +0100472 public SystemUpdatePolicy setFreezePeriods(List<FreezePeriod> freezePeriods) {
473 FreezePeriod.validatePeriods(freezePeriods);
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000474 mFreezePeriods.clear();
Rubin Xu1b2f3742018-03-28 14:54:08 +0100475 mFreezePeriods.addAll(freezePeriods);
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000476 return this;
477 }
478
479 /**
480 * Returns the list of freeze periods previously set on this system update policy object.
481 *
482 * @return the list of freeze periods, or an empty list if none was set.
483 */
Rubin Xu1b2f3742018-03-28 14:54:08 +0100484 public List<FreezePeriod> getFreezePeriods() {
485 return Collections.unmodifiableList(mFreezePeriods);
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000486 }
487
488 /**
489 * Returns the real calendar dates of the current freeze period, or null if the device
490 * is not in a freeze period at the moment.
491 * @hide
492 */
493 public Pair<LocalDate, LocalDate> getCurrentFreezePeriod(LocalDate now) {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100494 for (FreezePeriod interval : mFreezePeriods) {
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000495 if (interval.contains(now)) {
496 return interval.toCurrentOrFutureRealDates(now);
497 }
498 }
499 return null;
500 }
501
Rubin Xu658d7bb2018-02-07 14:51:43 +0000502 /**
503 * Returns time (in milliseconds) until the start of the next freeze period, assuming now
504 * is not within a freeze period.
505 */
506 private long timeUntilNextFreezePeriod(long now) {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100507 List<FreezePeriod> sortedPeriods = FreezePeriod.canonicalizePeriods(mFreezePeriods);
Rubin Xu658d7bb2018-02-07 14:51:43 +0000508 LocalDate nowDate = millisToDate(now);
509 LocalDate nextFreezeStart = null;
Rubin Xu1b2f3742018-03-28 14:54:08 +0100510 for (FreezePeriod interval : sortedPeriods) {
Rubin Xu658d7bb2018-02-07 14:51:43 +0000511 if (interval.after(nowDate)) {
512 nextFreezeStart = interval.toCurrentOrFutureRealDates(nowDate).first;
513 break;
514 } else if (interval.contains(nowDate)) {
515 throw new IllegalArgumentException("Given date is inside a freeze period");
516 }
517 }
518 if (nextFreezeStart == null) {
519 // If no interval is after now, then it must be the one that starts at the beginning
520 // of next year
521 nextFreezeStart = sortedPeriods.get(0).toCurrentOrFutureRealDates(nowDate).first;
522 }
523 return dateToMillis(nextFreezeStart) - now;
524 }
525
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000526 /** @hide */
527 public void validateFreezePeriods() {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100528 FreezePeriod.validatePeriods(mFreezePeriods);
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000529 }
530
531 /** @hide */
532 public void validateAgainstPreviousFreezePeriod(LocalDate prevPeriodStart,
533 LocalDate prevPeriodEnd, LocalDate now) {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100534 FreezePeriod.validateAgainstPreviousFreezePeriod(mFreezePeriods, prevPeriodStart,
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000535 prevPeriodEnd, now);
536 }
537
Rubin Xu658d7bb2018-02-07 14:51:43 +0000538 /**
539 * An installation option represents how system update clients should act on incoming system
540 * updates and how long this action is valid for, given the current system update policy. Its
541 * action could be one of the following
542 * <ul>
Rubin Xu1b2f3742018-03-28 14:54:08 +0100543 * <li> {@link #TYPE_INSTALL_AUTOMATIC} system updates should be installed immedately and
544 * without user intervention as soon as they become available.
545 * <li> {@link #TYPE_POSTPONE} system updates should be postponed for a maximum of 30 days
546 * <li> {@link #TYPE_PAUSE} system updates should be postponed indefinitely until further notice
Rubin Xu658d7bb2018-02-07 14:51:43 +0000547 * </ul>
548 *
549 * The effective time measures how long this installation option is valid for from the queried
550 * time, in milliseconds.
551 *
552 * This is an internal API for system update clients.
553 * @hide
554 */
555 @SystemApi
556 public static class InstallationOption {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100557 /** @hide */
558 @IntDef(prefix = { "TYPE_" }, value = {
559 TYPE_INSTALL_AUTOMATIC,
560 TYPE_PAUSE,
561 TYPE_POSTPONE
562 })
563 @Retention(RetentionPolicy.SOURCE)
564 @interface InstallationOptionType {}
565
566 @InstallationOptionType
Rubin Xu658d7bb2018-02-07 14:51:43 +0000567 private final int mType;
568 private long mEffectiveTime;
569
Rubin Xu1b2f3742018-03-28 14:54:08 +0100570 InstallationOption(@InstallationOptionType int type, long effectiveTime) {
Rubin Xu658d7bb2018-02-07 14:51:43 +0000571 this.mType = type;
572 this.mEffectiveTime = effectiveTime;
573 }
574
Rubin Xu1b2f3742018-03-28 14:54:08 +0100575 /**
576 * Returns the type of the current installation option, could be one of
577 * {@link #TYPE_INSTALL_AUTOMATIC}, {@link #TYPE_POSTPONE} and {@link #TYPE_PAUSE}.
578 * @return type of installation option.
579 */
580 public @InstallationOptionType int getType() {
Rubin Xu658d7bb2018-02-07 14:51:43 +0000581 return mType;
582 }
583
Rubin Xu1b2f3742018-03-28 14:54:08 +0100584 /**
585 * Returns how long the current installation option in effective for, starting from the time
586 * of query.
587 * @return the effective time in milliseconds.
588 */
Rubin Xu658d7bb2018-02-07 14:51:43 +0000589 public long getEffectiveTime() {
590 return mEffectiveTime;
591 }
592
593 /** @hide */
594 protected void limitEffectiveTime(long otherTime) {
595 mEffectiveTime = Long.min(mEffectiveTime, otherTime);
596 }
597 }
598
599 /**
600 * Returns the installation option at the specified time, under the current
601 * {@code SystemUpdatePolicy} object. This is a convenience method for system update clients
602 * so they can instantiate this policy at any given time and find out what to do with incoming
603 * system updates, without the need of examining the overall policy structure.
604 *
605 * Normally the system update clients will query the current installation option by calling this
606 * method with the current timestamp, and act on the returned option until its effective time
607 * lapses. It can then query the latest option using a new timestamp. It should also listen
608 * for {@code DevicePolicyManager#ACTION_SYSTEM_UPDATE_POLICY_CHANGED} broadcast, in case the
609 * whole policy is updated.
610 *
611 * @param when At what time the intallation option is being queried, specified in number of
612 milliseonds since the epoch.
613 * @see InstallationOption
614 * @hide
615 */
616 @SystemApi
617 public InstallationOption getInstallationOptionAt(long when) {
618 LocalDate whenDate = millisToDate(when);
619 Pair<LocalDate, LocalDate> current = getCurrentFreezePeriod(whenDate);
620 if (current != null) {
621 return new InstallationOption(TYPE_PAUSE,
622 dateToMillis(roundUpLeapDay(current.second).plusDays(1)) - when);
623 }
624 // We are not within a freeze period, query the underlying policy.
625 // But also consider the start of the next freeze period, which might
626 // reduce the effective time of the current installation option
627 InstallationOption option = getInstallationOptionRegardlessFreezeAt(when);
628 if (mFreezePeriods.size() > 0) {
629 option.limitEffectiveTime(timeUntilNextFreezePeriod(when));
630 }
631 return option;
632 }
633
634 private InstallationOption getInstallationOptionRegardlessFreezeAt(long when) {
635 if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) {
636 return new InstallationOption(mPolicyType, Long.MAX_VALUE);
637 } else if (mPolicyType == TYPE_INSTALL_WINDOWED) {
638 Calendar query = Calendar.getInstance();
639 query.setTimeInMillis(when);
640 // Calculate the number of milliseconds since midnight of the time specified by when
641 long whenMillis = TimeUnit.HOURS.toMillis(query.get(Calendar.HOUR_OF_DAY))
642 + TimeUnit.MINUTES.toMillis(query.get(Calendar.MINUTE))
643 + TimeUnit.SECONDS.toMillis(query.get(Calendar.SECOND))
644 + query.get(Calendar.MILLISECOND);
645 long windowStartMillis = TimeUnit.MINUTES.toMillis(mMaintenanceWindowStart);
646 long windowEndMillis = TimeUnit.MINUTES.toMillis(mMaintenanceWindowEnd);
647 final long dayInMillis = TimeUnit.DAYS.toMillis(1);
648
649 if ((windowStartMillis <= whenMillis && whenMillis <= windowEndMillis)
650 || ((windowStartMillis > windowEndMillis)
651 && (windowStartMillis <= whenMillis || whenMillis <= windowEndMillis))) {
652 return new InstallationOption(TYPE_INSTALL_AUTOMATIC,
653 (windowEndMillis - whenMillis + dayInMillis) % dayInMillis);
654 } else {
655 return new InstallationOption(TYPE_PAUSE,
656 (windowStartMillis - whenMillis + dayInMillis) % dayInMillis);
657 }
658 } else {
659 throw new RuntimeException("Unknown policy type");
660 }
661 }
662
663 private static LocalDate roundUpLeapDay(LocalDate date) {
664 if (date.isLeapYear() && date.getMonthValue() == 2 && date.getDayOfMonth() == 28) {
665 return date.plusDays(1);
666 } else {
667 return date;
668 }
669 }
670
671 /** Convert a timestamp since epoch to a LocalDate using default timezone, truncating
672 * the hour/min/seconds part.
673 */
674 private static LocalDate millisToDate(long when) {
675 return Instant.ofEpochMilli(when).atZone(ZoneId.systemDefault()).toLocalDate();
676 }
677
678 /**
679 * Returns the timestamp since epoch of a LocalDate, assuming the time is 00:00:00.
680 */
681 private static long dateToMillis(LocalDate when) {
682 return LocalDateTime.of(when, LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant()
683 .toEpochMilli();
684 }
685
Rubin Xu8027a4f2015-03-10 17:52:37 +0000686 @Override
687 public String toString() {
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000688 return String.format("SystemUpdatePolicy (type: %d, windowStart: %d, windowEnd: %d, "
689 + "freezes: [%s])",
690 mPolicyType, mMaintenanceWindowStart, mMaintenanceWindowEnd,
691 mFreezePeriods.stream().map(n -> n.toString()).collect(Collectors.joining(",")));
Rubin Xud86d58c2015-05-05 16:57:37 +0100692 }
693
694 @Override
695 public int describeContents() {
696 return 0;
697 }
698
699 @Override
700 public void writeToParcel(Parcel dest, int flags) {
701 dest.writeInt(mPolicyType);
702 dest.writeInt(mMaintenanceWindowStart);
703 dest.writeInt(mMaintenanceWindowEnd);
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000704 int freezeCount = mFreezePeriods.size();
705 dest.writeInt(freezeCount);
706 for (int i = 0; i < freezeCount; i++) {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100707 FreezePeriod interval = mFreezePeriods.get(i);
708 dest.writeInt(interval.getStart().getMonthValue());
709 dest.writeInt(interval.getStart().getDayOfMonth());
710 dest.writeInt(interval.getEnd().getMonthValue());
711 dest.writeInt(interval.getEnd().getDayOfMonth());
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000712 }
Rubin Xud86d58c2015-05-05 16:57:37 +0100713 }
714
715 public static final Parcelable.Creator<SystemUpdatePolicy> CREATOR =
716 new Parcelable.Creator<SystemUpdatePolicy>() {
717
718 @Override
719 public SystemUpdatePolicy createFromParcel(Parcel source) {
720 SystemUpdatePolicy policy = new SystemUpdatePolicy();
721 policy.mPolicyType = source.readInt();
722 policy.mMaintenanceWindowStart = source.readInt();
723 policy.mMaintenanceWindowEnd = source.readInt();
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000724 int freezeCount = source.readInt();
725 policy.mFreezePeriods.ensureCapacity(freezeCount);
726 for (int i = 0; i < freezeCount; i++) {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100727 MonthDay start = MonthDay.of(source.readInt(), source.readInt());
728 MonthDay end = MonthDay.of(source.readInt(), source.readInt());
729 policy.mFreezePeriods.add(new FreezePeriod(start, end));
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000730 }
Rubin Xud86d58c2015-05-05 16:57:37 +0100731 return policy;
732 }
733
734 @Override
735 public SystemUpdatePolicy[] newArray(int size) {
736 return new SystemUpdatePolicy[size];
737 }
738 };
739
Rubin Xud86d58c2015-05-05 16:57:37 +0100740 /**
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000741 * Restore a previously saved SystemUpdatePolicy from XML. No need to validate
742 * the reconstructed policy since the XML is supposed to be created by the
743 * system server from a validated policy object previously.
Rubin Xud86d58c2015-05-05 16:57:37 +0100744 * @hide
745 */
746 public static SystemUpdatePolicy restoreFromXml(XmlPullParser parser) {
747 try {
748 SystemUpdatePolicy policy = new SystemUpdatePolicy();
749 String value = parser.getAttributeValue(null, KEY_POLICY_TYPE);
750 if (value != null) {
751 policy.mPolicyType = Integer.parseInt(value);
752
753 value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_START);
754 if (value != null) {
755 policy.mMaintenanceWindowStart = Integer.parseInt(value);
756 }
757 value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_END);
758 if (value != null) {
759 policy.mMaintenanceWindowEnd = Integer.parseInt(value);
760 }
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000761
762 int outerDepth = parser.getDepth();
763 int type;
764 while ((type = parser.next()) != END_DOCUMENT
765 && (type != END_TAG || parser.getDepth() > outerDepth)) {
766 if (type == END_TAG || type == TEXT) {
767 continue;
768 }
769 if (!parser.getName().equals(KEY_FREEZE_TAG)) {
770 continue;
771 }
Rubin Xu1b2f3742018-03-28 14:54:08 +0100772 policy.mFreezePeriods.add(new FreezePeriod(
773 MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_START)),
774 MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_END))));
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000775 }
Rubin Xud86d58c2015-05-05 16:57:37 +0100776 return policy;
777 }
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000778 } catch (NumberFormatException | XmlPullParserException | IOException e) {
Rubin Xud86d58c2015-05-05 16:57:37 +0100779 // Fail through
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000780 Log.w(TAG, "Load xml failed", e);
Rubin Xud86d58c2015-05-05 16:57:37 +0100781 }
782 return null;
Rubin Xu8027a4f2015-03-10 17:52:37 +0000783 }
784
785 /**
Rubin Xud86d58c2015-05-05 16:57:37 +0100786 * @hide
Rubin Xu8027a4f2015-03-10 17:52:37 +0000787 */
Rubin Xud86d58c2015-05-05 16:57:37 +0100788 public void saveToXml(XmlSerializer out) throws IOException {
789 out.attribute(null, KEY_POLICY_TYPE, Integer.toString(mPolicyType));
790 out.attribute(null, KEY_INSTALL_WINDOW_START, Integer.toString(mMaintenanceWindowStart));
791 out.attribute(null, KEY_INSTALL_WINDOW_END, Integer.toString(mMaintenanceWindowEnd));
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000792 for (int i = 0; i < mFreezePeriods.size(); i++) {
Rubin Xu1b2f3742018-03-28 14:54:08 +0100793 FreezePeriod interval = mFreezePeriods.get(i);
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000794 out.startTag(null, KEY_FREEZE_TAG);
Rubin Xu1b2f3742018-03-28 14:54:08 +0100795 out.attribute(null, KEY_FREEZE_START, interval.getStart().toString());
796 out.attribute(null, KEY_FREEZE_END, interval.getEnd().toString());
Rubin Xu29b9a7d2018-01-11 09:24:02 +0000797 out.endTag(null, KEY_FREEZE_TAG);
798 }
Rubin Xu8027a4f2015-03-10 17:52:37 +0000799 }
800}
801