Tweak SystemUpdatePolicy according to API review.

Make SystemUpdatePolicy Parcelable; hide public constructor and
expose static builder methods.

Bug: 20820025
Change-Id: I594ba3c7e5514551134ba6c866b24498b66506bf
diff --git a/Android.mk b/Android.mk
index 27dedd9..264ad48 100644
--- a/Android.mk
+++ b/Android.mk
@@ -483,6 +483,7 @@
 	frameworks/base/graphics/java/android/graphics/Rect.aidl \
 	frameworks/base/core/java/android/accounts/AuthenticatorDescription.aidl \
 	frameworks/base/core/java/android/accounts/Account.aidl \
+	frameworks/base/core/java/android/app/admin/SystemUpdatePolicy.aidl \
 	frameworks/base/core/java/android/print/PrintDocumentInfo.aidl \
 	frameworks/base/core/java/android/print/PageRange.aidl \
 	frameworks/base/core/java/android/print/PrintAttributes.aidl \
diff --git a/api/current.txt b/api/current.txt
index c5e4bdd..d57587f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5905,23 +5905,21 @@
     field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
   }
 
-  public class SystemUpdatePolicy {
-    ctor public SystemUpdatePolicy();
+  public class SystemUpdatePolicy implements android.os.Parcelable {
+    method public static android.app.admin.SystemUpdatePolicy createAutomaticInstallPolicy();
+    method public static android.app.admin.SystemUpdatePolicy createPostponeInstallPolicy();
+    method public static android.app.admin.SystemUpdatePolicy createWindowedInstallPolicy(int, int);
+    method public int describeContents();
     method public int getInstallWindowEnd();
     method public int getInstallWindowStart();
     method public int getPolicyType();
-    method public void setAutomaticInstallPolicy();
-    method public void setPostponeInstallPolicy();
-    method public void setWindowedInstallPolicy(int, int) throws android.app.admin.SystemUpdatePolicy.InvalidWindowException;
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdatePolicy> CREATOR;
     field public static final int TYPE_INSTALL_AUTOMATIC = 1; // 0x1
     field public static final int TYPE_INSTALL_WINDOWED = 2; // 0x2
     field public static final int TYPE_POSTPONE = 3; // 0x3
   }
 
-  public static class SystemUpdatePolicy.InvalidWindowException extends java.lang.Exception {
-    ctor public SystemUpdatePolicy.InvalidWindowException(java.lang.String);
-  }
-
 }
 
 package android.app.backup {
diff --git a/api/system-current.txt b/api/system-current.txt
index 13796ff..39b8134 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6016,23 +6016,21 @@
     field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
   }
 
-  public class SystemUpdatePolicy {
-    ctor public SystemUpdatePolicy();
+  public class SystemUpdatePolicy implements android.os.Parcelable {
+    method public static android.app.admin.SystemUpdatePolicy createAutomaticInstallPolicy();
+    method public static android.app.admin.SystemUpdatePolicy createPostponeInstallPolicy();
+    method public static android.app.admin.SystemUpdatePolicy createWindowedInstallPolicy(int, int);
+    method public int describeContents();
     method public int getInstallWindowEnd();
     method public int getInstallWindowStart();
     method public int getPolicyType();
-    method public void setAutomaticInstallPolicy();
-    method public void setPostponeInstallPolicy();
-    method public void setWindowedInstallPolicy(int, int) throws android.app.admin.SystemUpdatePolicy.InvalidWindowException;
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdatePolicy> CREATOR;
     field public static final int TYPE_INSTALL_AUTOMATIC = 1; // 0x1
     field public static final int TYPE_INSTALL_WINDOWED = 2; // 0x2
     field public static final int TYPE_POSTPONE = 3; // 0x3
   }
 
-  public static class SystemUpdatePolicy.InvalidWindowException extends java.lang.Exception {
-    ctor public SystemUpdatePolicy.InvalidWindowException(java.lang.String);
-  }
-
 }
 
 package android.app.backup {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ae07206..3fb7059 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4253,11 +4253,7 @@
     public void setSystemUpdatePolicy(ComponentName who, SystemUpdatePolicy policy) {
         if (mService != null) {
             try {
-                if (policy != null) {
-                    mService.setSystemUpdatePolicy(who, policy.getPolicyBundle());
-                } else {
-                    mService.setSystemUpdatePolicy(who, null);
-                }
+                mService.setSystemUpdatePolicy(who, policy);
             } catch (RemoteException re) {
                 Log.w(TAG, "Error calling setSystemUpdatePolicy", re);
             }
@@ -4272,12 +4268,7 @@
     public SystemUpdatePolicy getSystemUpdatePolicy() {
         if (mService != null) {
             try {
-                PersistableBundle bundle = mService.getSystemUpdatePolicy();
-                if (bundle != null) {
-                    return new SystemUpdatePolicy(bundle);
-                } else {
-                    return null;
-                }
+                return mService.getSystemUpdatePolicy();
             } catch (RemoteException re) {
                 Log.w(TAG, "Error calling getSystemUpdatePolicy", re);
             }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index e81e7c1..481ff62 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -17,6 +17,7 @@
 
 package android.app.admin;
 
+import android.app.admin.SystemUpdatePolicy;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -221,8 +222,8 @@
     void setUserIcon(in ComponentName admin, in Bitmap icon);
 
     void sendDeviceInitializerStatus(int statusCode, String description);
-    void setSystemUpdatePolicy(in ComponentName who, in PersistableBundle policy);
-    PersistableBundle getSystemUpdatePolicy();
+    void setSystemUpdatePolicy(in ComponentName who, in SystemUpdatePolicy policy);
+    SystemUpdatePolicy getSystemUpdatePolicy();
 
     boolean setKeyguardDisabled(in ComponentName admin, boolean disabled);
     boolean setStatusBarDisabled(in ComponentName who, boolean disabled);
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.aidl b/core/java/android/app/admin/SystemUpdatePolicy.aidl
new file mode 100644
index 0000000..58e8d15
--- /dev/null
+++ b/core/java/android/app/admin/SystemUpdatePolicy.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.app.admin;
+
+parcelable SystemUpdatePolicy;
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java
index de56cd0..20ddb77 100644
--- a/core/java/android/app/admin/SystemUpdatePolicy.java
+++ b/core/java/android/app/admin/SystemUpdatePolicy.java
@@ -17,8 +17,15 @@
 package android.app.admin;
 
 import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.PersistableBundle;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -28,7 +35,7 @@
  * @see DevicePolicyManager#setSystemUpdatePolicy
  * @see DevicePolicyManager#getSystemUpdatePolicy
  */
-public class SystemUpdatePolicy {
+public class SystemUpdatePolicy implements Parcelable {
 
     /** @hide */
     @IntDef({
@@ -39,6 +46,10 @@
     @interface SystemUpdatePolicyType {}
 
     /**
+     * Unknown policy type, used only internally.
+     */
+    private static final int TYPE_UNKNOWN = -1;
+    /**
      * Install system update automatically as soon as one is available.
      */
     public static final int TYPE_INSTALL_AUTOMATIC = 1;
@@ -63,45 +74,40 @@
     private static final String KEY_POLICY_TYPE = "policy_type";
     private static final String KEY_INSTALL_WINDOW_START = "install_window_start";
     private static final String KEY_INSTALL_WINDOW_END = "install_window_end";
-
-    private PersistableBundle mPolicy;
-
-    public  SystemUpdatePolicy() {
-        mPolicy = new PersistableBundle();
-    }
-
     /**
-     * Construct an SystemUpdatePolicy object from a bundle.
-     * @hide
+     * The upper boundary of the daily maintenance window: 24 * 60 minutes.
      */
-    public SystemUpdatePolicy(PersistableBundle in) {
-        mPolicy = new PersistableBundle(in);
+    private static final int WINDOW_BOUNDARY = 24 * 60;
+
+    @SystemUpdatePolicyType
+    private int mPolicyType;
+
+    private int mMaintenanceWindowStart;
+    private int mMaintenanceWindowEnd;
+
+
+    private SystemUpdatePolicy() {
+        mPolicyType = TYPE_UNKNOWN;
     }
 
     /**
-     * Retrieve the underlying bundle where the policy is stored.
-     * @hide
-     */
-    public PersistableBundle getPolicyBundle() {
-        return new PersistableBundle(mPolicy);
-    }
-
-    /**
-     * Set the policy to: install update automatically as soon as one is available.
+     * Create a policy object and set it to install update automatically as soon as one is
+     * available.
      *
      * @see #TYPE_INSTALL_AUTOMATIC
      */
-    public void setAutomaticInstallPolicy() {
-        mPolicy.clear();
-        mPolicy.putInt(KEY_POLICY_TYPE, TYPE_INSTALL_AUTOMATIC);
+    public static SystemUpdatePolicy createAutomaticInstallPolicy() {
+        SystemUpdatePolicy policy = new SystemUpdatePolicy();
+        policy.mPolicyType = TYPE_INSTALL_AUTOMATIC;
+        return policy;
     }
 
     /**
-     * Set the policy to: new system update will only be installed automatically when the system
-     * clock is inside a daily maintenance window. If the start and end times are the same, the
-     * window is considered to include the WHOLE 24 hours, that is, updates can install at any time.
-     * If the given window in invalid, a {@link SystemUpdatePolicy.InvalidWindowException} will be
-     * thrown. If start time is later than end time, the window is considered spanning midnight,
+     * Create a policy object and set it to: new system update will only be installed automatically
+     * when the system clock is inside a daily maintenance window. If the start and end times are
+     * the same, the window is considered to include the WHOLE 24 hours, that is, updates can
+     * install at any time. If the given window in invalid, a {@link IllegalArgumentException} will
+     * be thrown. If start time is later than end time, the window is considered spanning midnight,
      * i.e. end time donates a time on the next day. The maintenance window will last for 30 days,
      * after which the system should revert back to its normal behavior as if no policy were set.
      *
@@ -111,25 +117,29 @@
      *            midnight in the device's local time. Must be in the range of [0, 1440).
      * @see #TYPE_INSTALL_WINDOWED
      */
-    public void setWindowedInstallPolicy(int startTime, int endTime) throws InvalidWindowException{
-        if (startTime < 0 || startTime >= 1440 || endTime < 0 || endTime >= 1440) {
-            throw new InvalidWindowException("startTime and endTime must be inside [0, 1440)");
+    public static SystemUpdatePolicy createWindowedInstallPolicy(int startTime, int endTime) {
+        if (startTime < 0 || startTime >= WINDOW_BOUNDARY
+                || endTime < 0 || endTime >= WINDOW_BOUNDARY) {
+            throw new IllegalArgumentException("startTime and endTime must be inside [0, 1440)");
         }
-        mPolicy.clear();
-        mPolicy.putInt(KEY_POLICY_TYPE, TYPE_INSTALL_WINDOWED);
-        mPolicy.putInt(KEY_INSTALL_WINDOW_START, startTime);
-        mPolicy.putInt(KEY_INSTALL_WINDOW_END, endTime);
+        SystemUpdatePolicy policy = new SystemUpdatePolicy();
+        policy.mPolicyType = TYPE_INSTALL_WINDOWED;
+        policy.mMaintenanceWindowStart = startTime;
+        policy.mMaintenanceWindowEnd = endTime;
+        return policy;
     }
 
     /**
-     * Set the policy to: block installation for a maximum period of 30 days. After expiration the
-     * system should revert back to its normal behavior as if no policy were set.
+     * Create a policy object and set it to block installation for a maximum period of 30 days.
+     * After expiration the system should revert back to its normal behavior as if no policy were
+     * set.
      *
      * @see #TYPE_POSTPONE
      */
-    public void setPostponeInstallPolicy() {
-        mPolicy.clear();
-        mPolicy.putInt(KEY_POLICY_TYPE, TYPE_POSTPONE);
+    public static SystemUpdatePolicy createPostponeInstallPolicy() {
+        SystemUpdatePolicy policy = new SystemUpdatePolicy();
+        policy.mPolicyType = TYPE_POSTPONE;
+        return policy;
     }
 
     /**
@@ -140,7 +150,7 @@
      */
     @SystemUpdatePolicyType
     public int getPolicyType() {
-        return mPolicy.getInt(KEY_POLICY_TYPE, -1);
+        return mPolicyType;
     }
 
     /**
@@ -150,8 +160,8 @@
      * or -1 if the policy does not have a maintenance window.
      */
     public int getInstallWindowStart() {
-        if (getPolicyType() == TYPE_INSTALL_WINDOWED) {
-            return mPolicy.getInt(KEY_INSTALL_WINDOW_START, -1);
+        if (mPolicyType == TYPE_INSTALL_WINDOWED) {
+            return mMaintenanceWindowStart;
         } else {
             return -1;
         }
@@ -164,26 +174,98 @@
      * or -1 if the policy does not have a maintenance window.
      */
     public int getInstallWindowEnd() {
-        if (getPolicyType() == TYPE_INSTALL_WINDOWED) {
-            return mPolicy.getInt(KEY_INSTALL_WINDOW_END, -1);
+        if (mPolicyType == TYPE_INSTALL_WINDOWED) {
+            return mMaintenanceWindowEnd;
         } else {
             return -1;
         }
     }
 
+    /**
+     * Return if this object represents a valid policy.
+     * @hide
+     */
+    public boolean isValid() {
+        if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) {
+            return true;
+        } else if (mPolicyType == TYPE_INSTALL_WINDOWED) {
+            return mMaintenanceWindowStart >= 0 && mMaintenanceWindowStart < WINDOW_BOUNDARY
+                    && mMaintenanceWindowEnd >= 0 && mMaintenanceWindowEnd < WINDOW_BOUNDARY;
+        } else {
+            return false;
+        }
+    }
+
     @Override
     public String toString() {
-        return mPolicy.toString();
+        return String.format("SystemUpdatePolicy (type: %d, windowStart: %d, windowEnd: %d)",
+                mPolicyType, mMaintenanceWindowStart, mMaintenanceWindowEnd);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mPolicyType);
+        dest.writeInt(mMaintenanceWindowStart);
+        dest.writeInt(mMaintenanceWindowEnd);
+    }
+
+    public static final Parcelable.Creator<SystemUpdatePolicy> CREATOR =
+            new Parcelable.Creator<SystemUpdatePolicy>() {
+
+                @Override
+                public SystemUpdatePolicy createFromParcel(Parcel source) {
+                    SystemUpdatePolicy policy = new SystemUpdatePolicy();
+                    policy.mPolicyType = source.readInt();
+                    policy.mMaintenanceWindowStart = source.readInt();
+                    policy.mMaintenanceWindowEnd = source.readInt();
+                    return policy;
+                }
+
+                @Override
+                public SystemUpdatePolicy[] newArray(int size) {
+                    return new SystemUpdatePolicy[size];
+                }
+    };
+
+
+    /**
+     * @hide
+     */
+    public static SystemUpdatePolicy restoreFromXml(XmlPullParser parser) {
+        try {
+            SystemUpdatePolicy policy = new SystemUpdatePolicy();
+            String value = parser.getAttributeValue(null, KEY_POLICY_TYPE);
+            if (value != null) {
+                policy.mPolicyType = Integer.parseInt(value);
+
+                value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_START);
+                if (value != null) {
+                    policy.mMaintenanceWindowStart = Integer.parseInt(value);
+                }
+                value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_END);
+                if (value != null) {
+                    policy.mMaintenanceWindowEnd = Integer.parseInt(value);
+                }
+                return policy;
+            }
+        } catch (NumberFormatException e) {
+            // Fail through
+        }
+        return null;
     }
 
     /**
-     * Exception thrown by {@link SystemUpdatePolicy#setWindowedInstallPolicy(int, int)} in case the
-     * specified window is invalid.
+     * @hide
      */
-    public static class InvalidWindowException extends Exception {
-        public InvalidWindowException(String reason) {
-            super(reason);
-        }
+    public void saveToXml(XmlSerializer out) throws IOException {
+        out.attribute(null, KEY_POLICY_TYPE, Integer.toString(mPolicyType));
+        out.attribute(null, KEY_INSTALL_WINDOW_START, Integer.toString(mMaintenanceWindowStart));
+        out.attribute(null, KEY_INSTALL_WINDOW_END, Integer.toString(mMaintenanceWindowEnd));
     }
 }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
index 28ffc57..88bf54e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
@@ -17,6 +17,7 @@
 package com.android.server.devicepolicy;
 
 import android.app.AppGlobals;
+import android.app.admin.SystemUpdatePolicy;
 import android.content.ComponentName;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -78,7 +79,7 @@
     private final HashMap<Integer, OwnerInfo> mProfileOwners = new HashMap<Integer, OwnerInfo>();
 
     // Local system update policy controllable by device owner.
-    private PersistableBundle mSystemUpdatePolicy;
+    private SystemUpdatePolicy mSystemUpdatePolicy;
 
     // Private default constructor.
     private DeviceOwner() {
@@ -192,11 +193,11 @@
         return mProfileOwners.keySet();
     }
 
-    PersistableBundle getSystemUpdatePolicy() {
+    SystemUpdatePolicy getSystemUpdatePolicy() {
         return mSystemUpdatePolicy;
     }
 
-    void setSystemUpdatePolicy(PersistableBundle systemUpdatePolicy) {
+    void setSystemUpdatePolicy(SystemUpdatePolicy systemUpdatePolicy) {
         mSystemUpdatePolicy = systemUpdatePolicy;
     }
 
@@ -291,7 +292,7 @@
                     }
                     mProfileOwners.put(userId, profileOwnerInfo);
                 } else if (TAG_SYSTEM_UPDATE_POLICY.equals(tag)) {
-                    mSystemUpdatePolicy = PersistableBundle.restoreFromXml(parser);
+                    mSystemUpdatePolicy = SystemUpdatePolicy.restoreFromXml(parser);
                 } else {
                     throw new XmlPullParserException(
                             "Unexpected tag in device owner file: " + tag);
@@ -361,11 +362,7 @@
             // Write system update policy tag
             if (mSystemUpdatePolicy != null) {
                 out.startTag(null, TAG_SYSTEM_UPDATE_POLICY);
-                try {
-                    mSystemUpdatePolicy.saveToXml(out);
-                } catch (XmlPullParserException e) {
-                    Slog.e(TAG, "Failed to save system update policy", e);
-                }
+                mSystemUpdatePolicy.saveToXml(out);
                 out.endTag(null, TAG_SYSTEM_UPDATE_POLICY);
             }
             out.endDocument();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 67c198f..12fcf81 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -39,6 +39,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.IDevicePolicyManager;
+import android.app.admin.SystemUpdatePolicy;
 import android.app.backup.IBackupManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -6238,7 +6239,10 @@
     }
 
     @Override
-    public void setSystemUpdatePolicy(ComponentName who, PersistableBundle policy) {
+    public void setSystemUpdatePolicy(ComponentName who, SystemUpdatePolicy policy) {
+        if (policy != null && !policy.isValid()) {
+            throw new IllegalArgumentException("Invalid system update policy.");
+        }
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
             if (policy == null) {
@@ -6254,9 +6258,14 @@
     }
 
     @Override
-    public PersistableBundle getSystemUpdatePolicy() {
+    public SystemUpdatePolicy getSystemUpdatePolicy() {
         synchronized (this) {
-            return mDeviceOwner.getSystemUpdatePolicy();
+            SystemUpdatePolicy policy =  mDeviceOwner.getSystemUpdatePolicy();
+            if (policy != null && !policy.isValid()) {
+                Slog.w(LOG_TAG, "Stored system update policy is invalid, return null instead.");
+                return null;
+            }
+            return policy;
         }
     }