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

Commit 430789ea authored by Rubin Xu's avatar Rubin Xu Committed by Android (Google) Code Review
Browse files

Merge changes I4d700bc4,I14fdac19,I66159f36 into udc-dev

* changes:
  Always sync user restriction state to UserManager
  Fix issue with upgrading from previous user versions.
  Properly store device policy user restrictions.
parents 0eb1805f 5394ddbe
Loading
Loading
Loading
Loading
+21 −9
Original line number Diff line number Diff line
@@ -2667,7 +2667,8 @@ public class UserManagerService extends IUserManager.Stub {
        }
    }

    private void setUserRestrictionInner(int userId, @NonNull String key, boolean value) {
    @VisibleForTesting
    void setUserRestrictionInner(int userId, @NonNull String key, boolean value) {
        if (!UserRestrictionsUtils.isValidRestriction(key)) {
            Slog.e(LOG_TAG, "Setting invalid restriction " + key);
            return;
@@ -3704,7 +3705,8 @@ public class UserManagerService extends IUserManager.Stub {
                    if (type == XmlPullParser.START_TAG) {
                        final String name = parser.getName();
                        if (name.equals(TAG_USER)) {
                            UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID));
                            UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID),
                                    mUserVersion);

                            if (userData != null) {
                                synchronized (mUsersLock) {
@@ -4277,11 +4279,11 @@ public class UserManagerService extends IUserManager.Stub {

            UserRestrictionsUtils.writeRestrictions(serializer,
                    mDevicePolicyUserRestrictions.getRestrictions(UserHandle.USER_ALL),
                    TAG_DEVICE_POLICY_RESTRICTIONS);
                    TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS);

            UserRestrictionsUtils.writeRestrictions(serializer,
                    mDevicePolicyUserRestrictions.getRestrictions(userInfo.id),
                    TAG_DEVICE_POLICY_RESTRICTIONS);
                    TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS);
        }

        if (userData.account != null) {
@@ -4385,7 +4387,7 @@ public class UserManagerService extends IUserManager.Stub {
    }

    @GuardedBy({"mPackagesLock"})
    private UserData readUserLP(int id) {
    private UserData readUserLP(int id, int userVersion) {
        try (ResilientAtomicFile file = getUserFile(id)) {
            FileInputStream fis = null;
            try {
@@ -4394,19 +4396,19 @@ public class UserManagerService extends IUserManager.Stub {
                    Slog.e(LOG_TAG, "User info not found, returning null, user id: " + id);
                    return null;
                }
                return readUserLP(id, fis);
                return readUserLP(id, fis, userVersion);
            } catch (Exception e) {
                // Remove corrupted file and retry.
                Slog.e(LOG_TAG, "Error reading user info, user id: " + id);
                file.failRead(fis, e);
                return readUserLP(id);
                return readUserLP(id, userVersion);
            }
        }
    }

    @GuardedBy({"mPackagesLock"})
    @VisibleForTesting
    UserData readUserLP(int id, InputStream is) throws IOException,
    UserData readUserLP(int id, InputStream is, int userVersion) throws IOException,
            XmlPullParserException {
        int flags = 0;
        String userType = null;
@@ -4499,7 +4501,17 @@ public class UserManagerService extends IUserManager.Stub {
                } else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) {
                    legacyLocalRestrictions = UserRestrictionsUtils.readRestrictions(parser);
                } else if (TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS.equals(tag)) {
                    if (userVersion < 10) {
                        // Prior to version 10, the local user restrictions were stored as sub tags
                        // grouped by the user id of the source user. The source is no longer stored
                        // on versions 10+ as this is now stored in the DevicePolicyEngine.
                        RestrictionsSet oldLocalRestrictions =
                                RestrictionsSet.readRestrictions(
                                    parser, TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS);
                        localRestrictions = oldLocalRestrictions.mergeAll();
                    } else {
                        localRestrictions = UserRestrictionsUtils.readRestrictions(parser);
                    }
                } else if (TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS.equals(tag)) {
                    globalRestrictions = UserRestrictionsUtils.readRestrictions(parser);
                } else if (TAG_ACCOUNT.equals(tag)) {
+66 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
import android.app.BroadcastOptions;
import android.app.admin.BooleanPolicyValue;
import android.app.admin.DevicePolicyIdentifiers;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyState;
@@ -133,6 +134,67 @@ final class DevicePolicyEngine {
        mEnforcingAdmins = new SparseArray<>();
    }

    private void maybeForceEnforcementRefreshLocked(@NonNull PolicyDefinition<?> policyDefinition) {
        try {
            if (shouldForceEnforcementRefresh(policyDefinition)) {
                // This is okay because it's only true for user restrictions which are all <Boolean>
                forceEnforcementRefreshLocked((PolicyDefinition<Boolean>) policyDefinition);
            }
        } catch (Throwable e) {
            // Catch any possible exceptions just to be on the safe side
            Log.e(TAG, "Exception throw during maybeForceEnforcementRefreshLocked", e);
        }
    }

    private boolean shouldForceEnforcementRefresh(@NonNull PolicyDefinition<?> policyDefinition) {
        // These are all "not nullable" but for the purposes of maximum safety for a lightly tested
        // change we check here
        if (policyDefinition == null) {
            return false;
        }
        PolicyKey policyKey = policyDefinition.getPolicyKey();
        if (policyKey == null) {
            return false;
        }

        if (policyKey instanceof UserRestrictionPolicyKey) {
            // b/307481299 We must force all user restrictions to re-sync local
            // + global on each set/clear
            return true;
        }

        return false;
    }

    private void forceEnforcementRefreshLocked(PolicyDefinition<Boolean> policyDefinition) {
        Binder.withCleanCallingIdentity(() -> {
            // Sync global state
            PolicyValue<Boolean> globalValue = new BooleanPolicyValue(false);
            try {
                PolicyState<Boolean> policyState = getGlobalPolicyStateLocked(policyDefinition);
                globalValue = policyState.getCurrentResolvedPolicy();
            } catch (IllegalArgumentException e) {
                // Expected for local-only policies
            }

            enforcePolicy(policyDefinition, globalValue, UserHandle.USER_ALL);

            // Loop through each user and sync that user's state
            for (UserInfo user : mUserManager.getUsers()) {
                PolicyValue<Boolean> localValue = new BooleanPolicyValue(false);
                try {
                    PolicyState<Boolean> localPolicyState = getLocalPolicyStateLocked(
                            policyDefinition, user.id);
                    localValue = localPolicyState.getCurrentResolvedPolicy();
                } catch (IllegalArgumentException e) {
                    // Expected for global-only policies
                }

                enforcePolicy(policyDefinition, localValue, user.id);
            }
        });
    }

    /**
     * Set the policy for the provided {@code policyDefinition} (see {@link PolicyDefinition}) and
     * {@code enforcingAdmin} to the provided {@code value}.
@@ -174,6 +236,7 @@ final class DevicePolicyEngine {
            // No need to notify admins as no new policy is actually enforced, we're just filling in
            // the data structures.
            if (!skipEnforcePolicy) {
                maybeForceEnforcementRefreshLocked(policyDefinition);
                if (policyChanged) {
                    onLocalPolicyChangedLocked(policyDefinition, enforcingAdmin, userId);
                }
@@ -262,6 +325,7 @@ final class DevicePolicyEngine {
        Objects.requireNonNull(enforcingAdmin);

        synchronized (mLock) {
            maybeForceEnforcementRefreshLocked(policyDefinition);
            if (!hasLocalPolicyLocked(policyDefinition, userId)) {
                return;
            }
@@ -425,6 +489,7 @@ final class DevicePolicyEngine {
            // No need to notify admins as no new policy is actually enforced, we're just filling in
            // the data structures.
            if (!skipEnforcePolicy) {
                maybeForceEnforcementRefreshLocked(policyDefinition);
                if (policyChanged) {
                    onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin);
                }
@@ -474,6 +539,7 @@ final class DevicePolicyEngine {
            PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
            boolean policyChanged = policyState.removePolicy(enforcingAdmin);

            maybeForceEnforcementRefreshLocked(policyDefinition);
            if (policyChanged) {
                onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin);
            }
+20 −0
Original line number Diff line number Diff line
<user id="100"
    serialNumber="0"
    flags="3091"
    type="android.os.usertype.full.SYSTEM"
    created="0"
    lastLoggedIn="0"
    lastLoggedInFingerprint="0"
    profileBadge="0">
  <restrictions no_oem_unlock="true" />
  <device_policy_local_restrictions>
    <restrictions_user user_id="0">
      <restrictions no_camera="true" />
    </restrictions_user>
    <restrictions_user user_id="100">
      <restrictions no_camera="true" />
      <restrictions no_install_unknown_sources="true" />
    </restrictions_user>
  </device_policy_local_restrictions>
  <ignorePrepareStorageErrors>false</ignorePrepareStorageErrors>
</user>
 No newline at end of file
+89 −1
Original line number Diff line number Diff line
@@ -43,27 +43,33 @@ import android.annotation.UserIdInt;
import android.app.PropertyInvalidatedCache;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo.UserInfoFlag;
import android.content.res.Resources;
import android.os.Looper;
import android.os.Parcel;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import android.text.TextUtils;
import android.util.Xml;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.frameworks.servicestests.R;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerService.UserData;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
@@ -76,6 +82,7 @@ import java.util.List;
@MediumTest
public class UserManagerServiceUserInfoTest {
    private UserManagerService mUserManagerService;
    private Resources mResources;

    @Before
    public void setup() {
@@ -95,6 +102,8 @@ public class UserManagerServiceUserInfoTest {
        assertEquals("Multiple users so this test can't run.", 1, users.size());
        assertEquals("Only user present isn't the system user.",
                UserHandle.USER_SYSTEM, users.get(0).id);

        mResources = InstrumentationRegistry.getTargetContext().getResources();
    }

    @Test
@@ -108,11 +117,51 @@ public class UserManagerServiceUserInfoTest {
        byte[] bytes = baos.toByteArray();

        UserData read = mUserManagerService.readUserLP(
                data.info.id, new ByteArrayInputStream(bytes));
                data.info.id, new ByteArrayInputStream(bytes), 0);

        assertUserInfoEquals(data.info, read.info, /* parcelCopy= */ false);
    }

    /** Tests that device policy restrictions are written/read properly. */
    @Test
    public void testWriteReadDevicePolicyUserRestrictions() throws Exception {
        final String globalRestriction = UserManager.DISALLOW_FACTORY_RESET;
        final String localRestriction = UserManager.DISALLOW_CONFIG_DATE_TIME;

        UserData data = new UserData();
        data.info = createUser(100, FLAG_FULL, "A type");

        mUserManagerService.putUserInfo(data.info);

        // Set a global and user restriction so they get written out to the user file.
        setUserRestrictions(data.info.id, globalRestriction, localRestriction, true);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream out = new DataOutputStream(baos);
        mUserManagerService.writeUserLP(data, out);
        byte[] bytes = baos.toByteArray();

        // Clear the restrictions to see if they are properly read in from the user file.
        setUserRestrictions(data.info.id, globalRestriction, localRestriction, false);

        final int userVersion = 10;
        //read the secondary and SYSTEM user file to fetch local/global device policy restrictions.
        mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(bytes),
                userVersion);

        assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(globalRestriction));
        assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(localRestriction));
    }

    /** Sets a global and local restriction and verifies they were set properly **/
    private void setUserRestrictions(int id, String global, String local, boolean enabled) {
        mUserManagerService.setUserRestrictionInner(UserHandle.USER_ALL, global, enabled);
        assertEquals(mUserManagerService.hasUserRestrictionOnAnyUser(global), enabled);

        mUserManagerService.setUserRestrictionInner(id, local, enabled);
        assertEquals(mUserManagerService.hasUserRestrictionOnAnyUser(local), enabled);
    }

    @Test
    public void testParcelUnparcelUserInfo() throws Exception {
        UserInfo info = createUser();
@@ -250,6 +299,45 @@ public class UserManagerServiceUserInfoTest {
        assertTrue(mUserManagerService.isUserOfType(106, USER_TYPE_FULL_DEMO));
    }

    /** Tests readUserLP upgrading from version 9 to 10+. */
    @Test
    public void testUserRestrictionsUpgradeFromV9() throws Exception {
        final String[] localRestrictions = new String[] {
            UserManager.DISALLOW_CAMERA,
            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
        };

        final int userId = 100;
        UserData data = new UserData();
        data.info = createUser(userId, FLAG_FULL, "A type");

        mUserManagerService.putUserInfo(data.info);

        for (String restriction : localRestrictions) {
            assertFalse(mUserManagerService.hasBaseUserRestriction(restriction, userId));
            assertFalse(mUserManagerService.hasUserRestriction(restriction, userId));
        }

        // Convert the xml resource to the system storage xml format.
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream os = new DataOutputStream(baos);
        XmlPullParser in = mResources.getXml(R.xml.user_100_v9);
        XmlSerializer out = Xml.newBinarySerializer();
        out.setOutput(os, StandardCharsets.UTF_8.name());
        Xml.copy(in, out);
        byte[] userBytes = baos.toByteArray();
        baos.reset();

        final int userVersion = 9;
        mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(userBytes),
                userVersion);

        for (String restriction : localRestrictions) {
            assertFalse(mUserManagerService.hasBaseUserRestriction(restriction, userId));
            assertTrue(mUserManagerService.hasUserRestriction(restriction, userId));
        }
    }

    /** Creates a UserInfo with the given flags and userType. */
    private UserInfo createUser(@UserIdInt int userId, @UserInfoFlag int flags, String userType) {
        return new UserInfo(userId, "A Name", "A path", flags, userType);