Modify restrictions bundle per api council recommendations

Use a Bundle for persisting and passing to the application, but use a
list to return data back from an application that's exposing restrictions.

Changed the xml reading/writing code to store the value type in the Bundle
so that it can be reproduced when reading. Earlier we were assuming only
String and String[].

Bug: 8633967

Change-Id: I523d5553728edcf28a1e9d432f490b4956f34215
diff --git a/api/current.txt b/api/current.txt
index 5a33193..9a0cf41 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5618,7 +5618,6 @@
     method public abstract java.lang.String[] fileList();
     method public abstract android.content.Context getApplicationContext();
     method public abstract android.content.pm.ApplicationInfo getApplicationInfo();
-    method public java.util.List<android.content.RestrictionEntry> getApplicationRestrictions();
     method public abstract android.content.res.AssetManager getAssets();
     method public abstract java.io.File getCacheDir();
     method public abstract java.lang.ClassLoader getClassLoader();
@@ -6205,8 +6204,9 @@
     field public static final java.lang.String EXTRA_REFERRER = "android.intent.extra.REFERRER";
     field public static final java.lang.String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token";
     field public static final java.lang.String EXTRA_REPLACING = "android.intent.extra.REPLACING";
-    field public static final java.lang.String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions";
+    field public static final java.lang.String EXTRA_RESTRICTIONS_BUNDLE = "android.intent.extra.restrictions_bundle";
     field public static final java.lang.String EXTRA_RESTRICTIONS_INTENT = "android.intent.extra.restrictions_intent";
+    field public static final java.lang.String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list";
     field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
     field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
     field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
@@ -17556,7 +17556,7 @@
   }
 
   public class UserManager {
-    method public static synchronized android.os.UserManager get(android.content.Context);
+    method public android.os.Bundle getApplicationRestrictions(java.lang.String);
     method public long getSerialNumberForUser(android.os.UserHandle);
     method public int getUserCount();
     method public android.os.UserHandle getUserForSerialNumber(long);
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 7b07438..0d7f0a7 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -134,11 +134,6 @@
         }
     }
 
-    public List<RestrictionEntry> getApplicationRestrictions() {
-        return ((UserManager) getSystemService(USER_SERVICE))
-                .getApplicationRestrictions(getPackageName(), android.os.Process.myUserHandle());
-    }
-
     public void registerComponentCallbacks(ComponentCallbacks callback) {
         synchronized (mComponentCallbacks) {
             mComponentCallbacks.add(callback);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 03e241a..5bd28b9 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -293,15 +293,6 @@
     public abstract Context getApplicationContext();
 
     /**
-     * Returns the list of restrictions for the application, or null if there are no
-     * restrictions.
-     * @return
-     */
-    public List<RestrictionEntry> getApplicationRestrictions() {
-        return getApplicationContext().getApplicationRestrictions();
-    }
-
-    /**
      * Add a new {@link ComponentCallbacks} to the base application of the
      * Context, which will be called at the same times as the ComponentCallbacks
      * methods of activities and other components are called.  Note that you
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 97ad7dd..1ab1eb8 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2417,11 +2417,16 @@
 
     /**
      * Broadcast to a specific application to query any supported restrictions to impose
-     * on restricted users. The response should contain an extra {@link #EXTRA_RESTRICTIONS},
+     * on restricted users. The broadcast intent contains an extra
+     * {@link #EXTRA_RESTRICTIONS_BUNDLE} with the currently persisted
+     * restrictions as a Bundle of key/value pairs. The value types can be Boolean, String or
+     * String[] depending on the restriction type.<p/>
+     * The response should contain an extra {@link #EXTRA_RESTRICTIONS_LIST},
      * which is of type <code>ArrayList&lt;RestrictionEntry&gt;</code>. It can also
      * contain an extra {@link #EXTRA_RESTRICTIONS_INTENT}, which is of type <code>Intent</code>.
      * The activity specified by that intent will be launched for a result which must contain
-     * the extra {@link #EXTRA_RESTRICTIONS}. The returned restrictions will be persisted.
+     * the extra {@link #EXTRA_RESTRICTIONS_LIST}. The keys and values of the returned restrictions
+     * will be persisted.
      * @see RestrictionEntry
      */
     public static final String ACTION_GET_RESTRICTION_ENTRIES =
@@ -3160,7 +3165,8 @@
         "android.intent.extra.ALLOW_MULTIPLE";
 
     /**
-     * The userHandle carried with broadcast intents related to addition, removal and switching of users
+     * The userHandle carried with broadcast intents related to addition, removal and switching of
+     * users
      * - {@link #ACTION_USER_ADDED}, {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}.
      * @hide
      */
@@ -3169,9 +3175,18 @@
 
     /**
      * Extra used in the response from a BroadcastReceiver that handles
-     * {@link #ACTION_GET_RESTRICTION_ENTRIES}.
+     * {@link #ACTION_GET_RESTRICTION_ENTRIES}. The type of the extra is
+     * <code>ArrayList&lt;RestrictionEntry&gt;</code>.
      */
-    public static final String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions";
+    public static final String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list";
+
+    /**
+     * Extra sent in the intent to the BroadcastReceiver that handles
+     * {@link #ACTION_GET_RESTRICTION_ENTRIES}. The type of the extra is a Bundle containing
+     * the restrictions as key/value pairs.
+     */
+    public static final String EXTRA_RESTRICTIONS_BUNDLE =
+            "android.intent.extra.restrictions_bundle";
 
     /**
      * Extra used in the response from a BroadcastReceiver that handles
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 2e8092a..a11358a 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -42,7 +42,8 @@
     int getUserHandle(int userSerialNumber);
     Bundle getUserRestrictions(int userHandle);
     void setUserRestrictions(in Bundle restrictions, int userHandle);
-    void setApplicationRestrictions(in String packageName, in List<RestrictionEntry> entries,
+    void setApplicationRestrictions(in String packageName, in Bundle restrictions,
             int userHandle);
-    List<RestrictionEntry> getApplicationRestrictions(in String packageName, int userHandle);
+    Bundle getApplicationRestrictions(in String packageName);
+    Bundle getApplicationRestrictionsForUser(in String packageName, int userHandle);
 }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index e580e2b..df065e9 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -142,6 +142,7 @@
 
     private static UserManager sInstance = null;
 
+    /** @hide */
     public synchronized static UserManager get(Context context) {
         if (sInstance == null) {
             sInstance = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -578,13 +579,29 @@
         return -1;
     }
 
+    /**
+     * Returns a Bundle containing any saved application restrictions for this user, for the
+     * given package name. Only an application with this package name can call this method.
+     * @param packageName the package name of the calling application
+     * @return a Bundle with the restrictions as key/value pairs, or null if there are no
+     * saved restrictions. The values can be of type Boolean, String or String[], depending
+     * on the restriction type, as defined by the application.
+     */
+    public Bundle getApplicationRestrictions(String packageName) {
+        try {
+            return mService.getApplicationRestrictions(packageName);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not get application restrictions for package " + packageName);
+        }
+        return null;
+    }
 
     /**
      * @hide
      */
-    public List<RestrictionEntry> getApplicationRestrictions(String packageName, UserHandle user) {
+    public Bundle getApplicationRestrictions(String packageName, UserHandle user) {
         try {
-            return mService.getApplicationRestrictions(packageName, user.getIdentifier());
+            return mService.getApplicationRestrictionsForUser(packageName, user.getIdentifier());
         } catch (RemoteException re) {
             Log.w(TAG, "Could not get application restrictions for user " + user.getIdentifier());
         }
@@ -594,10 +611,10 @@
     /**
      * @hide
      */
-    public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries,
+    public void setApplicationRestrictions(String packageName, Bundle restrictions,
             UserHandle user) {
         try {
-            mService.setApplicationRestrictions(packageName, entries, user.getIdentifier());
+            mService.setApplicationRestrictions(packageName, restrictions, user.getIdentifier());
         } catch (RemoteException re) {
             Log.w(TAG, "Could not set application restrictions for user " + user.getIdentifier());
         }
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index df90a56..11c6dab 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -88,8 +88,13 @@
     private static final String TAG_ENTRY = "entry";
     private static final String TAG_VALUE = "value";
     private static final String ATTR_KEY = "key";
+    private static final String ATTR_VALUE_TYPE = "type";
     private static final String ATTR_MULTIPLE = "m";
 
+    private static final String ATTR_TYPE_STRING_ARRAY = "sa";
+    private static final String ATTR_TYPE_STRING = "s";
+    private static final String ATTR_TYPE_BOOLEAN = "b";
+
     private static final String USER_INFO_DIR = "system" + File.separator + "users";
     private static final String USER_LIST_FILENAME = "userlist.xml";
     private static final String USER_PHOTO_FILENAME = "photo.png";
@@ -965,7 +970,12 @@
     }
 
     @Override
-    public List<RestrictionEntry> getApplicationRestrictions(String packageName, int userId) {
+    public Bundle getApplicationRestrictions(String packageName) {
+        return getApplicationRestrictionsForUser(packageName, UserHandle.getCallingUserId());
+    }
+
+    @Override
+    public Bundle getApplicationRestrictionsForUser(String packageName, int userId) {
         if (UserHandle.getCallingUserId() != userId
                 || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
             checkManageUsersPermission("Only system can get restrictions for other users/apps");
@@ -977,7 +987,7 @@
     }
 
     @Override
-    public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries,
+    public void setApplicationRestrictions(String packageName, Bundle restrictions,
             int userId) {
         if (UserHandle.getCallingUserId() != userId
                 || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
@@ -985,7 +995,7 @@
         }
         synchronized (mPackagesLock) {
             // Write the restrictions to XML
-            writeApplicationRestrictionsLocked(packageName, entries, userId);
+            writeApplicationRestrictionsLocked(packageName, restrictions, userId);
         }
     }
 
@@ -1001,9 +1011,9 @@
         }
     }
 
-    private List<RestrictionEntry> readApplicationRestrictionsLocked(String packageName,
+    private Bundle readApplicationRestrictionsLocked(String packageName,
             int userId) {
-        final ArrayList<RestrictionEntry> entries = new ArrayList<RestrictionEntry>();
+        final Bundle restrictions = new Bundle();
         final ArrayList<String> values = new ArrayList<String>();
 
         FileInputStream fis = null;
@@ -1023,12 +1033,13 @@
             if (type != XmlPullParser.START_TAG) {
                 Slog.e(LOG_TAG, "Unable to read restrictions file "
                         + restrictionsFile.getBaseFile());
-                return entries;
+                return restrictions;
             }
 
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
                     String key = parser.getAttributeValue(null, ATTR_KEY);
+                    String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE);
                     String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE);
                     if (multiple != null) {
                         int count = Integer.parseInt(multiple);
@@ -1041,14 +1052,13 @@
                         }
                         String [] valueStrings = new String[values.size()];
                         values.toArray(valueStrings);
-                        Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + valueStrings);
-                        RestrictionEntry entry = new RestrictionEntry(key, valueStrings);
-                        entries.add(entry);
+                        restrictions.putStringArray(key, valueStrings);
+                    } else if (ATTR_TYPE_BOOLEAN.equals(valType)) {
+                        restrictions.putBoolean(key, Boolean.parseBoolean(
+                                parser.nextText().trim()));
                     } else {
                         String value = parser.nextText().trim();
-                        Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + value);
-                        RestrictionEntry entry = new RestrictionEntry(key, value);
-                        entries.add(entry);
+                        restrictions.putString(key, value);
                     }
                 }
             }
@@ -1063,11 +1073,11 @@
                 }
             }
         }
-        return entries;
+        return restrictions;
     }
 
     private void writeApplicationRestrictionsLocked(String packageName,
-            List<RestrictionEntry> entries, int userId) {
+            Bundle restrictions, int userId) {
         FileOutputStream fos = null;
         AtomicFile restrictionsFile = new AtomicFile(
                 new File(Environment.getUserSystemDirectory(userId),
@@ -1084,18 +1094,24 @@
 
             serializer.startTag(null, TAG_RESTRICTIONS);
 
-            for (RestrictionEntry entry : entries) {
+            for (String key : restrictions.keySet()) {
+                Object value = restrictions.get(key);
                 serializer.startTag(null, TAG_ENTRY);
-                serializer.attribute(null, ATTR_KEY, entry.getKey());
-                if (entry.getSelectedString() != null || entry.getAllSelectedStrings() == null) {
-                    String value = entry.getSelectedString();
-                    serializer.text(value != null ? value : "");
+                serializer.attribute(null, ATTR_KEY, key);
+
+                if (value instanceof Boolean) {
+                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN);
+                    serializer.text(value.toString());
+                } else if (value == null || value instanceof String) {
+                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING);
+                    serializer.text(value != null ? (String) value : "");
                 } else {
-                    String[] values = entry.getAllSelectedStrings();
+                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING_ARRAY);
+                    String[] values = (String[]) value;
                     serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length));
-                    for (String value : values) {
+                    for (String choice : values) {
                         serializer.startTag(null, TAG_VALUE);
-                        serializer.text(value != null ? value : "");
+                        serializer.text(choice != null ? choice : "");
                         serializer.endTag(null, TAG_VALUE);
                     }
                 }