1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.pm;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.testng.Assert.assertThrows;
22 
23 import android.content.pm.UserProperties;
24 import android.os.Parcel;
25 import android.platform.test.annotations.Presubmit;
26 import android.util.Xml;
27 
28 import androidx.test.filters.MediumTest;
29 import androidx.test.runner.AndroidJUnit4;
30 
31 import com.android.modules.utils.TypedXmlPullParser;
32 import com.android.modules.utils.TypedXmlSerializer;
33 
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 
37 import java.io.ByteArrayInputStream;
38 import java.io.ByteArrayOutputStream;
39 import java.nio.charset.StandardCharsets;
40 import java.util.function.Supplier;
41 
42 /**
43  * Tests for UserManager's {@link UserProperties}.
44  *
45  * Additional test coverage (that actually exercises the functionality) can be found in
46  * {@link UserManagerTest} and
47  * {@link UserManagerServiceUserTypeTest} (for {@link UserProperties#updateFromXml}).
48  *
49  * <p>Run with: atest UserManagerServiceUserPropertiesTest
50  */
51 @Presubmit
52 @RunWith(AndroidJUnit4.class)
53 @MediumTest
54 public class UserManagerServiceUserPropertiesTest {
55 
56     /** Test that UserProperties can properly read the xml information that it writes. */
57     @Test
testWriteReadXml()58     public void testWriteReadXml() throws Exception {
59         final UserProperties defaultProps = new UserProperties.Builder()
60                 .setShowInLauncher(21)
61                 .setStartWithParent(false)
62                 .setShowInSettings(45)
63                 .setInheritDevicePolicy(67)
64                 .setUseParentsContacts(false)
65                 .setCrossProfileIntentFilterAccessControl(10)
66                 .setCrossProfileIntentResolutionStrategy(0)
67                 .setMediaSharedWithParent(false)
68                 .setCredentialShareableWithParent(true)
69                 .setDeleteAppWithParent(false)
70                 .build();
71         final UserProperties actualProps = new UserProperties(defaultProps);
72         actualProps.setShowInLauncher(14);
73         actualProps.setShowInSettings(32);
74         actualProps.setInheritDevicePolicy(51);
75         actualProps.setUseParentsContacts(true);
76         actualProps.setCrossProfileIntentFilterAccessControl(20);
77         actualProps.setCrossProfileIntentResolutionStrategy(1);
78         actualProps.setMediaSharedWithParent(true);
79         actualProps.setCredentialShareableWithParent(false);
80         actualProps.setDeleteAppWithParent(true);
81 
82         // Write the properties to xml.
83         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
84         final TypedXmlSerializer out = Xml.newFastSerializer();
85         out.setOutput(baos, StandardCharsets.UTF_8.name());
86         out.startDocument(null, true);
87         out.startTag(null, "testTag");
88         actualProps.writeToXml(out);
89         out.endTag(null, "testTag");
90         out.endDocument();
91 
92         // Now read those properties from xml.
93         final ByteArrayInputStream input = new ByteArrayInputStream(baos.toByteArray());
94         final TypedXmlPullParser parser = Xml.newFastPullParser();
95         parser.setInput(input, StandardCharsets.UTF_8.name());
96         parser.nextTag();
97         final UserProperties readProps = new UserProperties(parser, defaultProps);
98 
99         assertUserPropertiesEquals(actualProps, readProps);
100     }
101 
102     /** Tests parcelling an object in which all properties are present. */
103     @Test
testParcelUnparcel()104     public void testParcelUnparcel() throws Exception {
105         final UserProperties originalProps = new UserProperties.Builder()
106                 .setShowInLauncher(2145)
107                 .build();
108         final UserProperties readProps = parcelThenUnparcel(originalProps);
109         assertUserPropertiesEquals(originalProps, readProps);
110     }
111 
112     /** Tests copying a UserProperties object varying permissions. */
113     @Test
testCopyLacksPermissions()114     public void testCopyLacksPermissions() throws Exception {
115         final UserProperties defaultProps = new UserProperties.Builder()
116                 .setShowInLauncher(2145)
117                 .setStartWithParent(true)
118                 .setShowInSettings(3452)
119                 .setInheritDevicePolicy(1732)
120                 .setMediaSharedWithParent(true)
121                 .setDeleteAppWithParent(true)
122                 .build();
123         final UserProperties orig = new UserProperties(defaultProps);
124         orig.setShowInLauncher(2841);
125         orig.setStartWithParent(false);
126         orig.setShowInSettings(1437);
127         orig.setInheritDevicePolicy(9456);
128         orig.setDeleteAppWithParent(false);
129 
130         // Test every permission level. (Currently, it's linear so it's easy.)
131         for (int permLevel = 0; permLevel < 4; permLevel++) {
132             final boolean exposeAll = permLevel >= 3;
133             final boolean hasManage = permLevel >= 2;
134             final boolean hasQuery = permLevel >= 1;
135 
136             // Make a possibly-not-full-permission (i.e. partial) copy and check that it is correct.
137             final UserProperties copy = new UserProperties(orig, exposeAll, hasManage, hasQuery);
138             verifyTestCopyLacksPermissions(orig, copy, exposeAll, hasManage, hasQuery);
139             if (permLevel < 1) {
140                 // PropertiesPresent should definitely be different since not all items were copied.
141                 assertThat(orig.getPropertiesPresent()).isNotEqualTo(copy.getPropertiesPresent());
142             }
143 
144             // Now, just like in the SystemServer, parcel/unparcel the copy and make sure that the
145             // unparcelled version behaves just like the partial copy did.
146             final UserProperties readProps = parcelThenUnparcel(copy);
147             verifyTestCopyLacksPermissions(orig, readProps, exposeAll, hasManage, hasQuery);
148         }
149     }
150 
151     /**
152      * Verifies that the copy of orig has the expected properties
153      * for the test {@link #testCopyLacksPermissions}.
154      */
verifyTestCopyLacksPermissions( UserProperties orig, UserProperties copy, boolean exposeAll, boolean hasManagePermission, boolean hasQueryPermission)155     private void verifyTestCopyLacksPermissions(
156             UserProperties orig,
157             UserProperties copy,
158             boolean exposeAll,
159             boolean hasManagePermission,
160             boolean hasQueryPermission) {
161 
162         // Items requiring exposeAll.
163         assertEqualGetterOrThrows(orig::getStartWithParent, copy::getStartWithParent, exposeAll);
164         assertEqualGetterOrThrows(orig::getInheritDevicePolicy,
165                 copy::getInheritDevicePolicy, exposeAll);
166         assertEqualGetterOrThrows(orig::getCrossProfileIntentFilterAccessControl,
167                 copy::getCrossProfileIntentFilterAccessControl, exposeAll);
168         assertEqualGetterOrThrows(orig::getCrossProfileIntentResolutionStrategy,
169                 copy::getCrossProfileIntentResolutionStrategy, exposeAll);
170         assertEqualGetterOrThrows(orig::getDeleteAppWithParent,
171                 copy::getDeleteAppWithParent, exposeAll);
172 
173         // Items requiring hasManagePermission - put them here using hasManagePermission.
174         assertEqualGetterOrThrows(orig::getShowInSettings, copy::getShowInSettings,
175                 hasManagePermission);
176         assertEqualGetterOrThrows(orig::getUseParentsContacts,
177                 copy::getUseParentsContacts, hasManagePermission);
178 
179         // Items requiring hasQueryPermission - put them here using hasQueryPermission.
180 
181         // Items with no permission requirements.
182         assertEqualGetterOrThrows(orig::getShowInLauncher, copy::getShowInLauncher, true);
183         assertEqualGetterOrThrows(orig::isMediaSharedWithParent,
184                 copy::isMediaSharedWithParent, true);
185         assertEqualGetterOrThrows(orig::isCredentialShareableWithParent,
186                 copy::isCredentialShareableWithParent, true);
187     }
188 
189     /**
190      * If hasPerm, then asserts that value of actualGetter equals value of expectedGetter.
191      * If !hasPerm, then asserts that actualGetter throws a SecurityException.
192      */
193     @SuppressWarnings("ReturnValueIgnored")
assertEqualGetterOrThrows( Supplier expectedGetter, Supplier actualGetter, boolean hasPerm)194     private void assertEqualGetterOrThrows(
195             Supplier expectedGetter,
196             Supplier actualGetter,
197             boolean hasPerm) {
198         if (hasPerm) {
199             assertThat(expectedGetter.get()).isEqualTo(actualGetter.get());
200         } else {
201             assertThrows(SecurityException.class, actualGetter::get);
202         }
203     }
204 
parcelThenUnparcel(UserProperties originalProps)205     private UserProperties parcelThenUnparcel(UserProperties originalProps) {
206         final Parcel out = Parcel.obtain();
207         originalProps.writeToParcel(out, 0);
208         final byte[] data = out.marshall();
209         out.recycle();
210 
211         final Parcel in = Parcel.obtain();
212         in.unmarshall(data, 0, data.length);
213         in.setDataPosition(0);
214         final UserProperties readProps = UserProperties.CREATOR.createFromParcel(in);
215         in.recycle();
216 
217         return readProps;
218     }
219 
220     /** Checks that two UserProperties get the same values. */
assertUserPropertiesEquals(UserProperties expected, UserProperties actual)221     private void assertUserPropertiesEquals(UserProperties expected, UserProperties actual) {
222         assertThat(expected.getPropertiesPresent()).isEqualTo(actual.getPropertiesPresent());
223         assertThat(expected.getShowInLauncher()).isEqualTo(actual.getShowInLauncher());
224         assertThat(expected.getStartWithParent()).isEqualTo(actual.getStartWithParent());
225         assertThat(expected.getShowInSettings()).isEqualTo(actual.getShowInSettings());
226         assertThat(expected.getInheritDevicePolicy()).isEqualTo(actual.getInheritDevicePolicy());
227         assertThat(expected.getUseParentsContacts()).isEqualTo(actual.getUseParentsContacts());
228         assertThat(expected.getCrossProfileIntentFilterAccessControl())
229                 .isEqualTo(actual.getCrossProfileIntentFilterAccessControl());
230         assertThat(expected.getCrossProfileIntentResolutionStrategy())
231                 .isEqualTo(actual.getCrossProfileIntentResolutionStrategy());
232         assertThat(expected.isMediaSharedWithParent())
233                 .isEqualTo(actual.isMediaSharedWithParent());
234         assertThat(expected.isCredentialShareableWithParent())
235                 .isEqualTo(actual.isCredentialShareableWithParent());
236         assertThat(expected.getDeleteAppWithParent()).isEqualTo(actual.getDeleteAppWithParent());
237     }
238 }
239