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

Commit c444d10c authored by Alex Johnston's avatar Alex Johnston Committed by Android (Google) Code Review
Browse files

Merge "Change the data structure of restrictions in UM" into rvc-dev

parents 5eed7459 d4c416fd
Loading
Loading
Loading
Loading
+256 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.server.pm;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.os.Bundle;
import android.os.UserManager;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;

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

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

/**
 * Data structure that contains the mapping of users to user restrictions (either the user
 * restrictions that apply to them, or the user restrictions that they set, depending on the
 * circumstances).
 *
 * @hide
 */
public class RestrictionsSet {

    private static final String USER_ID = "user_id";
    private static final String TAG_RESTRICTIONS = "restrictions";
    private static final String TAG_RESTRICTIONS_USER = "restrictions_user";

    /**
     * Mapping of user restrictions.
     * Only non-empty restriction bundles are stored.
     * The key is the user id of the user.
     * userId -> restrictionBundle
     */
    private final SparseArray<Bundle> mUserRestrictions = new SparseArray<>(0);

    public RestrictionsSet() {
    }

    public RestrictionsSet(@UserIdInt int userId, @NonNull Bundle restrictions) {
        if (restrictions.isEmpty()) {
            throw new IllegalArgumentException("empty restriction bundle cannot be added.");
        }
        mUserRestrictions.put(userId, restrictions);
    }

    /**
     * Updates restriction bundle for a given user.
     * If new bundle is empty, record is removed from the array.
     *
     * @return whether restrictions bundle is different from the old one.
     */
    public boolean updateRestrictions(@UserIdInt int userId, @Nullable Bundle restrictions) {
        final boolean changed =
                !UserRestrictionsUtils.areEqual(mUserRestrictions.get(userId), restrictions);
        if (!changed) {
            return false;
        }
        if (!UserRestrictionsUtils.isEmpty(restrictions)) {
            mUserRestrictions.put(userId, restrictions);
        } else {
            mUserRestrictions.delete(userId);
        }
        return true;
    }

    /**
     * Moves a particular restriction from one restriction set to another, e.g. for all users.
     */
    public void moveRestriction(@NonNull RestrictionsSet destRestrictions, String restriction) {
        for (int i = 0; i < mUserRestrictions.size(); i++) {
            final int userId = mUserRestrictions.keyAt(i);
            final Bundle from = mUserRestrictions.valueAt(i);

            if (UserRestrictionsUtils.contains(from, restriction)) {
                from.remove(restriction);
                Bundle to = destRestrictions.getRestrictions(userId);
                if (to == null) {
                    to = new Bundle();
                    to.putBoolean(restriction, true);
                    destRestrictions.updateRestrictions(userId, to);
                } else {
                    to.putBoolean(restriction, true);
                }
                // Don't keep empty bundles.
                if (from.isEmpty()) {
                    mUserRestrictions.removeAt(i);
                    i--;
                }
            }
        }
    }

    /**
     * @return whether restrictions set has no restrictions.
     */
    public boolean isEmpty() {
        return mUserRestrictions.size() == 0;
    }

    /**
     * Merge all restrictions in restrictions set into one bundle. The original user restrictions
     * set does not get modified, instead a new bundle is returned.
     *
     * @return restrictions bundle containing all user restrictions.
     */
    public @NonNull Bundle mergeAll() {
        final Bundle result = new Bundle();
        for (int i = 0; i < mUserRestrictions.size(); i++) {
            UserRestrictionsUtils.merge(result, mUserRestrictions.valueAt(i));
        }
        return result;
    }

    /**
     * @return list of enforcing users that enforce a particular restriction.
     */
    public @NonNull List<UserManager.EnforcingUser> getEnforcingUsers(String restriction,
            @UserIdInt int deviceOwnerUserId) {
        final List<UserManager.EnforcingUser> result = new ArrayList<>();
        for (int i = 0; i < mUserRestrictions.size(); i++) {
            if (UserRestrictionsUtils.contains(mUserRestrictions.valueAt(i), restriction)) {
                result.add(getEnforcingUser(mUserRestrictions.keyAt(i), deviceOwnerUserId));
            }
        }
        return result;
    }

    private UserManager.EnforcingUser getEnforcingUser(@UserIdInt int userId,
            @UserIdInt int deviceOwnerUserId) {
        int source = deviceOwnerUserId == userId
                ? UserManager.RESTRICTION_SOURCE_DEVICE_OWNER
                : UserManager.RESTRICTION_SOURCE_PROFILE_OWNER;
        return new UserManager.EnforcingUser(userId, source);
    }

    /**
     * @return list of user restrictions for a given user. Null is returned if the user does not
     * have any restrictions.
     */
    public @Nullable Bundle getRestrictions(@UserIdInt int userId) {
        return mUserRestrictions.get(userId);
    }

    /**
     * Removes a given user from the restrictions set, returning true if the user has non-empty
     * restrictions before removal.
     */
    public boolean remove(@UserIdInt int userId) {
        boolean hasUserRestriction = mUserRestrictions.contains(userId);
        mUserRestrictions.remove(userId);
        return hasUserRestriction;
    }

    /**
     * Remove list of users and user restrictions.
     */
    public void removeAllRestrictions() {
        mUserRestrictions.clear();
    }

    /**
     * Serialize a given {@link RestrictionsSet} to XML.
     */
    public void writeRestrictions(@NonNull XmlSerializer serializer, @NonNull String outerTag)
            throws IOException {
        serializer.startTag(null, outerTag);
        for (int i = 0; i < mUserRestrictions.size(); i++) {
            serializer.startTag(null, TAG_RESTRICTIONS_USER);
            serializer.attribute(null, USER_ID, String.valueOf(mUserRestrictions.keyAt(i)));
            UserRestrictionsUtils.writeRestrictions(serializer, mUserRestrictions.valueAt(i),
                    TAG_RESTRICTIONS);
            serializer.endTag(null, TAG_RESTRICTIONS_USER);
        }
        serializer.endTag(null, outerTag);
    }

    /**
     * Read restrictions from XML.
     */
    public static RestrictionsSet readRestrictions(@NonNull XmlPullParser parser,
            @NonNull String outerTag) throws IOException, XmlPullParserException {
        RestrictionsSet restrictionsSet = new RestrictionsSet();
        int userId = 0;
        int type;

        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
            String tag = parser.getName();
            if (type == XmlPullParser.END_TAG && outerTag.equals(tag)) {
                return restrictionsSet;
            } else if (type == XmlPullParser.START_TAG && TAG_RESTRICTIONS_USER.equals(tag)) {
                userId = Integer.parseInt(parser.getAttributeValue(null, USER_ID));
            } else if (type == XmlPullParser.START_TAG && TAG_RESTRICTIONS.equals(tag)) {
                Bundle restrictions = UserRestrictionsUtils.readRestrictions(parser);
                restrictionsSet.updateRestrictions(userId, restrictions);
            }
        }
        throw new XmlPullParserException("restrictions cannot be read as xml is malformed.");
    }

    /**
     * Dumps {@link RestrictionsSet}.
     */
    public void dumpRestrictions(PrintWriter pw, String prefix) {
        boolean noneSet = true;
        for (int i = 0; i < mUserRestrictions.size(); i++) {
            pw.println(prefix + "User Id: " + mUserRestrictions.keyAt(i));
            UserRestrictionsUtils.dumpRestrictions(pw, prefix + "  ", mUserRestrictions.valueAt(i));
            noneSet = false;
        }
        if (noneSet) {
            pw.println(prefix + "none");
        }
    }

    public boolean containsKey(@UserIdInt int userId) {
        return mUserRestrictions.contains(userId);
    }

    @VisibleForTesting
    public int size() {
        return mUserRestrictions.size();
    }

    @VisibleForTesting
    public int keyAt(int index) {
        return mUserRestrictions.keyAt(index);
    }

    @VisibleForTesting
    public Bundle valueAt(int index) {
        return mUserRestrictions.valueAt(index);
    }

}
Loading