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

Commit f0d456b0 authored by Jason Parks's avatar Jason Parks Committed by Rubin Xu
Browse files

Fix issue with upgrading from previous user versions.

The storage format of users changed in userVersion 10. When upgrading
from userVersion 9 (Android 13), local user restrictions would be read
as base restrictions. This fix properly reads in the old version and
converts it to the new storage format.

Bug: 311687929
Bug: 305455828
Test: atest UserManagerServiceUserInfoTest
Change-Id: I14fdac19b4458c6bd2ccee7d1b119bc710e1b600
Merged-In: I14fdac19b4458c6bd2ccee7d1b119bc710e1b600
(cherry picked from commit 73928109)
parent c747c3fd
Loading
Loading
Loading
Loading
+17 −6
Original line number Diff line number Diff line
@@ -3705,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) {
@@ -4386,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 {
@@ -4395,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;
@@ -4500,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)) {
+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
+54 −2
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,7 +117,7 @@ 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);
    }
@@ -135,7 +144,11 @@ public class UserManagerServiceUserInfoTest {
        // Clear the restrictions to see if they are properly read in from the user file.
        setUserRestrictions(data.info.id, globalRestriction, localRestriction, false);

        mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(bytes));
        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));
    }
@@ -286,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);