From 5d204d1ca15bca69bd0003176d2fbf4e435204cc Mon Sep 17 00:00:00 2001 From: Jason Parks <jparks@google.com> Date: Tue, 31 Oct 2023 20:17:21 +0000 Subject: [PATCH] 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: 305455828 Test: atest UserManagerServiceUserInfoTest (cherry picked from commit 347b8a00c8aad4ce36e0c63e0c4c03b7fc86bb92) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:739281096aba494151f8c953f2d63ec9fd4c7e87) Merged-In: I14fdac19b4458c6bd2ccee7d1b119bc710e1b600 Change-Id: I14fdac19b4458c6bd2ccee7d1b119bc710e1b600 --- .../android/server/pm/UserManagerService.java | 23 ++++++-- .../servicestests/res/xml/user_100_v9.xml | 20 +++++++ .../pm/UserManagerServiceUserInfoTest.java | 56 ++++++++++++++++++- 3 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 services/tests/servicestests/res/xml/user_100_v9.xml diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d2929aef8a63b..a959fc10db49f 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -3706,7 +3706,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) { @@ -4387,7 +4388,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 { @@ -4396,19 +4397,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; @@ -4501,7 +4502,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)) { - localRestrictions = UserRestrictionsUtils.readRestrictions(parser); + 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)) { diff --git a/services/tests/servicestests/res/xml/user_100_v9.xml b/services/tests/servicestests/res/xml/user_100_v9.xml new file mode 100644 index 0000000000000..03c08ed408289 --- /dev/null +++ b/services/tests/servicestests/res/xml/user_100_v9.xml @@ -0,0 +1,20 @@ +<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 diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java index 9f75cf8d552ef..429c58e768bf4 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java @@ -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); -- GitLab