Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 654875fa authored by Fyodor Kupolov's avatar Fyodor Kupolov Committed by Android (Google) Code Review
Browse files

Merge "Support for nested bundles in setApplicationRestrictions"

parents 095d9990 262f9952
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -8379,6 +8379,7 @@ package android.content {
    ctor public RestrictionEntry(java.lang.String, boolean);
    ctor public RestrictionEntry(java.lang.String, java.lang.String[]);
    ctor public RestrictionEntry(java.lang.String, int);
    ctor public RestrictionEntry(java.lang.String, android.content.RestrictionEntry[], boolean);
    ctor public RestrictionEntry(android.os.Parcel);
    method public int describeContents();
    method public java.lang.String[] getAllSelectedStrings();
@@ -8387,6 +8388,7 @@ package android.content {
    method public java.lang.String getDescription();
    method public int getIntValue();
    method public java.lang.String getKey();
    method public android.content.RestrictionEntry[] getRestrictions();
    method public boolean getSelectedState();
    method public java.lang.String getSelectedString();
    method public java.lang.String getTitle();
@@ -8398,6 +8400,7 @@ package android.content {
    method public void setChoiceValues(android.content.Context, int);
    method public void setDescription(java.lang.String);
    method public void setIntValue(int);
    method public void setRestrictions(android.content.RestrictionEntry[]);
    method public void setSelectedState(boolean);
    method public void setSelectedString(java.lang.String);
    method public void setTitle(java.lang.String);
@@ -8405,6 +8408,8 @@ package android.content {
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.content.RestrictionEntry> CREATOR;
    field public static final int TYPE_BOOLEAN = 1; // 0x1
    field public static final int TYPE_BUNDLE = 7; // 0x7
    field public static final int TYPE_BUNDLE_ARRAY = 8; // 0x8
    field public static final int TYPE_CHOICE = 2; // 0x2
    field public static final int TYPE_INTEGER = 5; // 0x5
    field public static final int TYPE_MULTI_SELECT = 4; // 0x4
@@ -8413,6 +8418,7 @@ package android.content {
  }
  public class RestrictionsManager {
    method public static android.os.Bundle convertRestrictionsToBundle(java.util.List<android.content.RestrictionEntry>);
    method public android.content.Intent createLocalApprovalIntent();
    method public android.os.Bundle getApplicationRestrictions();
    method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String);
+6 −0
Original line number Diff line number Diff line
@@ -8600,6 +8600,7 @@ package android.content {
    ctor public RestrictionEntry(java.lang.String, boolean);
    ctor public RestrictionEntry(java.lang.String, java.lang.String[]);
    ctor public RestrictionEntry(java.lang.String, int);
    ctor public RestrictionEntry(java.lang.String, android.content.RestrictionEntry[], boolean);
    ctor public RestrictionEntry(android.os.Parcel);
    method public int describeContents();
    method public java.lang.String[] getAllSelectedStrings();
@@ -8608,6 +8609,7 @@ package android.content {
    method public java.lang.String getDescription();
    method public int getIntValue();
    method public java.lang.String getKey();
    method public android.content.RestrictionEntry[] getRestrictions();
    method public boolean getSelectedState();
    method public java.lang.String getSelectedString();
    method public java.lang.String getTitle();
@@ -8619,6 +8621,7 @@ package android.content {
    method public void setChoiceValues(android.content.Context, int);
    method public void setDescription(java.lang.String);
    method public void setIntValue(int);
    method public void setRestrictions(android.content.RestrictionEntry[]);
    method public void setSelectedState(boolean);
    method public void setSelectedString(java.lang.String);
    method public void setTitle(java.lang.String);
@@ -8626,6 +8629,8 @@ package android.content {
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.content.RestrictionEntry> CREATOR;
    field public static final int TYPE_BOOLEAN = 1; // 0x1
    field public static final int TYPE_BUNDLE = 7; // 0x7
    field public static final int TYPE_BUNDLE_ARRAY = 8; // 0x8
    field public static final int TYPE_CHOICE = 2; // 0x2
    field public static final int TYPE_INTEGER = 5; // 0x5
    field public static final int TYPE_MULTI_SELECT = 4; // 0x4
@@ -8634,6 +8639,7 @@ package android.content {
  }
  public class RestrictionsManager {
    method public static android.os.Bundle convertRestrictionsToBundle(java.util.List<android.content.RestrictionEntry>);
    method public android.content.Intent createLocalApprovalIntent();
    method public android.os.Bundle getApplicationRestrictions();
    method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String);
+123 −54
Original line number Diff line number Diff line
@@ -20,6 +20,9 @@ import android.annotation.ArrayRes;
import android.os.Parcel;
import android.os.Parcelable;

import java.util.Arrays;
import java.util.Objects;

/**
 * Applications can expose restrictions for a restricted user on a
 * multiuser device. The administrator can configure these restrictions that will then be
@@ -33,19 +36,19 @@ import android.os.Parcelable;
public class RestrictionEntry implements Parcelable {

    /**
     * A type of restriction. Use this type for information that needs to be transferred across
     * but shouldn't be presented to the user in the UI. Stores a single String value.
     * Hidden restriction type. Use this type for information that needs to be transferred
     * across but shouldn't be presented to the user in the UI. Stores a single String value.
     */
    public static final int TYPE_NULL         = 0;

    /**
     * A type of restriction. Use this for storing a boolean value, typically presented as
     * Restriction of type "bool". Use this for storing a boolean value, typically presented as
     * a checkbox in the UI.
     */
    public static final int TYPE_BOOLEAN      = 1;

    /**
     * A type of restriction. Use this for storing a string value, typically presented as
     * Restriction of type "choice". Use this for storing a string value, typically presented as
     * a single-select list. Call {@link #setChoiceEntries(String[])} and
     * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
     * and the corresponding values, respectively.
@@ -53,7 +56,7 @@ public class RestrictionEntry implements Parcelable {
    public static final int TYPE_CHOICE       = 2;

    /**
     * A type of restriction. Use this for storing a string value, typically presented as
     * Internal restriction type. Use this for storing a string value, typically presented as
     * a single-select list. Call {@link #setChoiceEntries(String[])} and
     * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
     * and the corresponding values, respectively.
@@ -64,8 +67,8 @@ public class RestrictionEntry implements Parcelable {
    public static final int TYPE_CHOICE_LEVEL = 3;

    /**
     * A type of restriction. Use this for presenting a multi-select list where more than one
     * entry can be selected, such as for choosing specific titles to white-list.
     * Restriction of type "multi-select". Use this for presenting a multi-select list where more
     * than one entry can be selected, such as for choosing specific titles to white-list.
     * Call {@link #setChoiceEntries(String[])} and
     * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
     * and the corresponding values, respectively.
@@ -75,18 +78,30 @@ public class RestrictionEntry implements Parcelable {
    public static final int TYPE_MULTI_SELECT = 4;

    /**
     * A type of restriction. Use this for storing an integer value. The range of values
     * Restriction of type "integer". Use this for storing an integer value. The range of values
     * is from {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}.
     */
    public static final int TYPE_INTEGER = 5;

    /**
     * A type of restriction. Use this for storing a string value.
     * Restriction of type "string". Use this for storing a string value.
     * @see #setSelectedString
     * @see #getSelectedString
     */
    public static final int TYPE_STRING = 6;

    /**
     * Restriction of type "bundle". Use this for storing {@link android.os.Bundle bundles} of
     * restrictions
     */
    public static final int TYPE_BUNDLE = 7;

    /**
     * Restriction of type "bundle_array". Use this for storing arrays of
     * {@link android.os.Bundle bundles} of restrictions
     */
    public static final int TYPE_BUNDLE_ARRAY = 8;

    /** The type of restriction. */
    private int mType;

@@ -114,6 +129,12 @@ public class RestrictionEntry implements Parcelable {
    /* List of selected choices in the multi-select case. */
    private String[] mCurrentValues;

    /**
     * List of nested restrictions. Used by {@link #TYPE_BUNDLE bundle} and
     * {@link #TYPE_BUNDLE_ARRAY bundle_array} restrictions.
     */
    private RestrictionEntry[] mRestrictions;

    /**
     * Constructor for specifying the type and key, with no initial value;
     *
@@ -169,6 +190,35 @@ public class RestrictionEntry implements Parcelable {
        setIntValue(selectedInt);
    }

    /**
     * Constructor for {@link #TYPE_BUNDLE}/{@link #TYPE_BUNDLE_ARRAY} type.
     * @param key the unique key for this restriction
     * @param restrictionEntries array of nested restriction entries. If the entry, being created
     * represents a {@link #TYPE_BUNDLE_ARRAY bundle-array}, {@code restrictionEntries} array may
     * only contain elements of type {@link #TYPE_BUNDLE bundle}.
     * @param isBundleArray true if this restriction represents
     * {@link #TYPE_BUNDLE_ARRAY bundle-array} type, otherwise the type will be set to
     * {@link #TYPE_BUNDLE bundle}.
     */
    public RestrictionEntry(String key, RestrictionEntry[] restrictionEntries,
            boolean isBundleArray) {
        mKey = key;
        if (isBundleArray) {
            mType = TYPE_BUNDLE_ARRAY;
            if (restrictionEntries != null) {
                for (RestrictionEntry restriction : restrictionEntries) {
                    if (restriction.getType() != TYPE_BUNDLE) {
                        throw new IllegalArgumentException("bundle_array restriction can only have "
                                + "nested restriction entries of type bundle");
                    }
                }
            }
        } else {
            mType = TYPE_BUNDLE;
        }
        setRestrictions(restrictionEntries);
    }

    /**
     * Sets the type for this restriction.
     * @param type the type for this restriction.
@@ -282,6 +332,22 @@ public class RestrictionEntry implements Parcelable {
        mChoiceValues = context.getResources().getStringArray(stringArrayResId);
    }

    /**
     * Returns array of possible restriction entries that this entry may contain.
     */
    public RestrictionEntry[] getRestrictions() {
        return mRestrictions;
    }

   /**
    * Sets an array of possible restriction entries, that this entry may contain.
    * <p>This method is only relevant for types {@link #TYPE_BUNDLE} and
    * {@link #TYPE_BUNDLE_ARRAY}
    */
    public void setRestrictions(RestrictionEntry[] restrictions) {
        mRestrictions = restrictions;
    }

    /**
     * Returns the list of possible string values set earlier.
     * @return the list of possible values.
@@ -362,27 +428,30 @@ public class RestrictionEntry implements Parcelable {
        this.mTitle = title;
    }

    private boolean equalArrays(String[] one, String[] other) {
        if (one.length != other.length) return false;
        for (int i = 0; i < one.length; i++) {
            if (!one[i].equals(other[i])) return false;
        }
        return true;
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof RestrictionEntry)) return false;
        final RestrictionEntry other = (RestrictionEntry) o;
        // Make sure that either currentValue matches or currentValues matches.
        return mType == other.mType && mKey.equals(other.mKey)
                &&
                ((mCurrentValues == null && other.mCurrentValues == null
                  && mCurrentValue != null && mCurrentValue.equals(other.mCurrentValue))
                 ||
                 (mCurrentValue == null && other.mCurrentValue == null
                  && mCurrentValues != null && equalArrays(mCurrentValues, other.mCurrentValues)));
        if (mType != other.mType || mKey.equals(other.mKey)) {
            return false;
        }
        if (mCurrentValues == null && other.mCurrentValues == null
                && mRestrictions == null && other.mRestrictions == null
                && Objects.equals(mCurrentValue, other.mCurrentValue)) {
            return true;
        }
        if (mCurrentValue == null && other.mCurrentValue == null
                && mRestrictions == null && other.mRestrictions == null
                && Arrays.equals(mCurrentValues, other.mCurrentValues)) {
            return true;
        }
        if (mCurrentValue == null && other.mCurrentValue == null
                && mCurrentValue == null && other.mCurrentValue == null
                && Arrays.equals(mRestrictions, other.mRestrictions)) {
            return true;
        }
        return false;
    }

    @Override
@@ -397,28 +466,28 @@ public class RestrictionEntry implements Parcelable {
                    result = 31 * result + value.hashCode();
                }
            }
        } else if (mRestrictions != null) {
            result = 31 * result + Arrays.hashCode(mRestrictions);
        }
        return result;
    }

    private String[] readArray(Parcel in) {
        int count = in.readInt();
        String[] values = new String[count];
        for (int i = 0; i < count; i++) {
            values[i] = in.readString();
        }
        return values;
    }

    public RestrictionEntry(Parcel in) {
        mType = in.readInt();
        mKey = in.readString();
        mTitle = in.readString();
        mDescription = in.readString();
        mChoiceEntries = readArray(in);
        mChoiceValues = readArray(in);
        mChoiceEntries = in.readStringArray();
        mChoiceValues = in.readStringArray();
        mCurrentValue = in.readString();
        mCurrentValues = readArray(in);
        mCurrentValues = in.readStringArray();
        Parcelable[] parcelables = in.readParcelableArray(null);
        if (parcelables != null) {
            mRestrictions = new RestrictionEntry[parcelables.length];
            for (int i = 0; i < parcelables.length; i++) {
                mRestrictions[i] = (RestrictionEntry) parcelables[i];
            }
        }
    }

    @Override
@@ -426,27 +495,17 @@ public class RestrictionEntry implements Parcelable {
        return 0;
    }

    private void writeArray(Parcel dest, String[] values) {
        if (values == null) {
            dest.writeInt(0);
        } else {
            dest.writeInt(values.length);
            for (int i = 0; i < values.length; i++) {
                dest.writeString(values[i]);
            }
        }
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mType);
        dest.writeString(mKey);
        dest.writeString(mTitle);
        dest.writeString(mDescription);
        writeArray(dest, mChoiceEntries);
        writeArray(dest, mChoiceValues);
        dest.writeStringArray(mChoiceEntries);
        dest.writeStringArray(mChoiceValues);
        dest.writeString(mCurrentValue);
        writeArray(dest, mCurrentValues);
        dest.writeStringArray(mCurrentValues);
        dest.writeParcelableArray(mRestrictions, 0);
    }

    public static final Creator<RestrictionEntry> CREATOR = new Creator<RestrictionEntry>() {
@@ -461,6 +520,16 @@ public class RestrictionEntry implements Parcelable {

    @Override
    public String toString() {
        return "RestrictionsEntry {type=" + mType + ", key=" + mKey + ", value=" + mCurrentValue + "}";
        return "RestrictionEntry{" +
                "mType=" + mType +
                ", mKey='" + mKey + '\'' +
                ", mTitle='" + mTitle + '\'' +
                ", mDescription='" + mDescription + '\'' +
                ", mChoiceEntries=" + Arrays.toString(mChoiceEntries) +
                ", mChoiceValues=" + Arrays.toString(mChoiceValues) +
                ", mCurrentValue='" + mCurrentValue + '\'' +
                ", mCurrentValues=" + Arrays.toString(mCurrentValues) +
                ", mRestrictions=" + Arrays.toString(mRestrictions) +
                '}';
    }
}
+112 −18
Original line number Diff line number Diff line
@@ -32,12 +32,14 @@ import android.util.Log;
import android.util.Xml;

import com.android.internal.R;
import com.android.internal.util.XmlUtils;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
@@ -71,12 +73,15 @@ import java.util.List;
 *         android:key="string"
 *         android:title="string resource"
 *         android:restrictionType=["bool" | "string" | "integer"
 *                                         | "choice" | "multi-select" | "hidden"]
 *                                         | "choice" | "multi-select" | "hidden"
 *                                         | "bundle" | "bundle_array"]
 *         android:description="string resource"
 *         android:entries="string-array resource"
 *         android:entryValues="string-array resource"
 *         android:defaultValue="reference"
 *         /&gt;
 *         android:defaultValue="reference" &gt;
 *             &lt;restriction ... /&gt;
 *             ...
 *     &lt;/restriction&gt;
 *     &lt;restriction ... /&gt;
 *     ...
 * &lt;/restrictions&gt;
@@ -97,6 +102,9 @@ import java.util.List;
 * administrator controlling the values, if the title is not sufficient.</li>
 * </ul>
 * <p>
 * Only restrictions of type {@code bundle} and {@code bundle_array} can have one or multiple nested
 * restriction elements.
 * <p>
 * In your manifest's <code>application</code> section, add the meta-data tag to point to
 * the restrictions XML file as shown below:
 * <pre>
@@ -537,9 +545,7 @@ public class RestrictionsManager {

        XmlResourceParser xml =
                appInfo.loadXmlMetaData(mContext.getPackageManager(), META_DATA_APP_RESTRICTIONS);
        List<RestrictionEntry> restrictions = loadManifestRestrictions(packageName, xml);

        return restrictions;
        return loadManifestRestrictions(packageName, xml);
    }

    private List<RestrictionEntry> loadManifestRestrictions(String packageName,
@@ -550,25 +556,18 @@ public class RestrictionsManager {
        } catch (NameNotFoundException nnfe) {
            return null;
        }
        ArrayList<RestrictionEntry> restrictions = new ArrayList<RestrictionEntry>();
        ArrayList<RestrictionEntry> restrictions = new ArrayList<>();
        RestrictionEntry restriction;

        try {
            int tagType = xml.next();
            while (tagType != XmlPullParser.END_DOCUMENT) {
                if (tagType == XmlPullParser.START_TAG) {
                    if (xml.getName().equals(TAG_RESTRICTION)) {
                        AttributeSet attrSet = Xml.asAttributeSet(xml);
                        if (attrSet != null) {
                            TypedArray a = appContext.obtainStyledAttributes(attrSet,
                                    com.android.internal.R.styleable.RestrictionEntry);
                            restriction = loadRestriction(appContext, a);
                    restriction = loadRestrictionElement(appContext, xml);
                    if (restriction != null) {
                        restrictions.add(restriction);
                    }
                }
                    }
                }
                tagType = xml.next();
            }
        } catch (XmlPullParserException e) {
@@ -582,7 +581,21 @@ public class RestrictionsManager {
        return restrictions;
    }

    private RestrictionEntry loadRestriction(Context appContext, TypedArray a) {
    private RestrictionEntry loadRestrictionElement(Context appContext, XmlResourceParser xml)
            throws IOException, XmlPullParserException {
        if (xml.getName().equals(TAG_RESTRICTION)) {
            AttributeSet attrSet = Xml.asAttributeSet(xml);
            if (attrSet != null) {
                TypedArray a = appContext.obtainStyledAttributes(attrSet,
                        com.android.internal.R.styleable.RestrictionEntry);
                return loadRestriction(appContext, a, xml);
            }
        }
        return null;
    }

    private RestrictionEntry loadRestriction(Context appContext, TypedArray a, XmlResourceParser xml)
            throws IOException, XmlPullParserException {
        String key = a.getString(R.styleable.RestrictionEntry_key);
        int restrictionType = a.getInt(
                R.styleable.RestrictionEntry_restrictionType, -1);
@@ -633,9 +646,90 @@ public class RestrictionsManager {
                restriction.setSelectedState(
                        a.getBoolean(R.styleable.RestrictionEntry_defaultValue, false));
                break;
            case RestrictionEntry.TYPE_BUNDLE:
            case RestrictionEntry.TYPE_BUNDLE_ARRAY:
                final int outerDepth = xml.getDepth();
                List<RestrictionEntry> restrictionEntries = new ArrayList<>();
                while (XmlUtils.nextElementWithin(xml, outerDepth)) {
                    RestrictionEntry childEntry = loadRestrictionElement(appContext, xml);
                    if (childEntry == null) {
                        Log.w(TAG, "Child entry cannot be loaded for bundle restriction " + key);
                    } else {
                        restrictionEntries.add(childEntry);
                        if (restrictionType == RestrictionEntry.TYPE_BUNDLE_ARRAY
                                && childEntry.getType() != RestrictionEntry.TYPE_BUNDLE) {
                            Log.w(TAG, "bundle_array " + key
                                    + " can only contain entries of type bundle");
                        }
                    }
                }
                restriction.setRestrictions(restrictionEntries.toArray(new RestrictionEntry[
                        restrictionEntries.size()]));
                break;
            default:
                Log.w(TAG, "Unknown restriction type " + restrictionType);
        }
        return restriction;
    }

    /**
     * Converts a list of restrictions to the corresponding bundle, using the following mapping:
     * <table>
     *     <tr><th>RestrictionEntry</th><th>Bundle</th></tr>
     *     <tr><td>{@link RestrictionEntry#TYPE_BOOLEAN}</td><td>{@link Bundle#putBoolean}</td></tr>
     *     <tr><td>{@link RestrictionEntry#TYPE_CHOICE}, {@link RestrictionEntry#TYPE_CHOICE}</td>
     *     <td>{@link Bundle#putStringArray}</td></tr>
     *     <tr><td>{@link RestrictionEntry#TYPE_INTEGER}</td><td>{@link Bundle#putInt}</td></tr>
     *     <tr><td>{@link RestrictionEntry#TYPE_STRING}</td><td>{@link Bundle#putString}</td></tr>
     *     <tr><td>{@link RestrictionEntry#TYPE_BUNDLE}</td><td>{@link Bundle#putBundle}</td></tr>
     *     <tr><td>{@link RestrictionEntry#TYPE_BUNDLE_ARRAY}</td>
     *     <td>{@link Bundle#putParcelableArray}</td></tr>
     * </table>
     * @param entries list of restrictions
     */
    public static Bundle convertRestrictionsToBundle(List<RestrictionEntry> entries) {
        final Bundle bundle = new Bundle();
        for (RestrictionEntry entry : entries) {
            addRestrictionToBundle(bundle, entry);
        }
        return bundle;
    }

    private static Bundle addRestrictionToBundle(Bundle bundle, RestrictionEntry entry) {
        switch (entry.getType()) {
            case RestrictionEntry.TYPE_BOOLEAN:
                bundle.putBoolean(entry.getKey(), entry.getSelectedState());
                break;
            case RestrictionEntry.TYPE_CHOICE:
            case RestrictionEntry.TYPE_CHOICE_LEVEL:
            case RestrictionEntry.TYPE_MULTI_SELECT:
                bundle.putStringArray(entry.getKey(), entry.getAllSelectedStrings());
                break;
            case RestrictionEntry.TYPE_INTEGER:
                bundle.putInt(entry.getKey(), entry.getIntValue());
                break;
            case RestrictionEntry.TYPE_STRING:
            case RestrictionEntry.TYPE_NULL:
                bundle.putString(entry.getKey(), entry.getSelectedString());
                break;
            case RestrictionEntry.TYPE_BUNDLE:
                RestrictionEntry[] restrictions = entry.getRestrictions();
                Bundle childBundle = convertRestrictionsToBundle(Arrays.asList(restrictions));
                bundle.putBundle(entry.getKey(), childBundle);
                break;
            case RestrictionEntry.TYPE_BUNDLE_ARRAY:
                restrictions = entry.getRestrictions();
                Bundle[] bundleArray = new Bundle[restrictions.length];
                for (int i = 0; i < restrictions.length; i++) {
                    bundleArray[i] = addRestrictionToBundle(new Bundle(), restrictions[i]);
                }
                bundle.putParcelableArray(entry.getKey(), bundleArray);
                break;
            default:
                throw new IllegalArgumentException(
                        "Unsupported restrictionEntry type: " + entry.getType());
        }
        return bundle;
    }

}
+2 −0
Original line number Diff line number Diff line
@@ -7759,6 +7759,8 @@
            <enum name="multi-select" value="4" />
            <enum name="integer" value="5" />
            <enum name="string" value="6" />
            <enum name="bundle" value="7" />
            <enum name="bundle_array" value="8" />
        </attr>
        <attr name="title" />
        <attr name="description" />
Loading