1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.server.slice;
16 
17 import static org.junit.Assert.assertEquals;
18 import static org.junit.Assert.assertFalse;
19 import static org.junit.Assert.assertTrue;
20 import static org.mockito.ArgumentMatchers.eq;
21 import static org.mockito.Mockito.clearInvocations;
22 import static org.mockito.Mockito.mock;
23 import static org.mockito.Mockito.verify;
24 
25 import android.content.ContentResolver;
26 import android.net.Uri;
27 import android.testing.AndroidTestingRunner;
28 import android.testing.TestableLooper.RunWithLooper;
29 import android.util.Xml.Encoding;
30 
31 import androidx.test.filters.SmallTest;
32 
33 import com.android.server.UiServiceTestCase;
34 import com.android.server.slice.SliceClientPermissions.SliceAuthority;
35 import com.android.server.slice.SlicePermissionManager.PkgUser;
36 
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 import org.xmlpull.v1.XmlPullParser;
40 import org.xmlpull.v1.XmlPullParserException;
41 import org.xmlpull.v1.XmlPullParserFactory;
42 import org.xmlpull.v1.XmlSerializer;
43 
44 import java.io.ByteArrayInputStream;
45 import java.io.ByteArrayOutputStream;
46 import java.io.IOException;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.Comparator;
50 
51 @SmallTest
52 @RunWith(AndroidTestingRunner.class)
53 @RunWithLooper
54 public class SliceClientPermissionsTest extends UiServiceTestCase {
55 
56     @Test
testRemoveBasic()57     public void testRemoveBasic() {
58         PkgUser pkg = new PkgUser("com.android.pkg", 0);
59         DirtyTracker tracker = mock(DirtyTracker.class);
60         SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
61         Uri base = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
62                 .authority("com.android.pkg.slices").build();
63 
64         PkgUser testPkg = new PkgUser("other", 2);
65 
66         client.grantUri(base.buildUpon()
67                 .appendPath("first")
68                 .build(), testPkg);
69         client.revokeUri(base.buildUpon()
70                 .appendPath("first")
71                 .build(), testPkg);
72 
73         assertFalse(client.hasPermission(base.buildUpon()
74                 .appendPath("first")
75                 .appendPath("third")
76                 .build(), testPkg.getUserId()));
77 
78         ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
79         assertEquals(0, authorities.get(0).getPaths().size());
80     }
81 
82     @Test
testRemoveSubtrees()83     public void testRemoveSubtrees() {
84         PkgUser pkg = new PkgUser("com.android.pkg", 0);
85         DirtyTracker tracker = mock(DirtyTracker.class);
86         SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
87         Uri base = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
88                 .authority("com.android.pkg.slices").build();
89 
90         PkgUser testPkg = new PkgUser("other", 2);
91 
92         client.grantUri(base.buildUpon()
93                 .appendPath("first")
94                 .appendPath("second")
95                 .build(), testPkg);
96         client.grantUri(base.buildUpon()
97                 .appendPath("first")
98                 .appendPath("third")
99                 .build(), testPkg);
100         client.revokeUri(base.buildUpon()
101                 .appendPath("first")
102                 .build(), testPkg);
103 
104         assertFalse(client.hasPermission(base.buildUpon()
105                 .appendPath("first")
106                 .appendPath("fourth")
107                 .build(), testPkg.getUserId()));
108 
109         ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
110         assertEquals(0, authorities.get(0).getPaths().size());
111     }
112 
113     @Test
testAddConsolidate_addFirst()114     public void testAddConsolidate_addFirst() {
115         PkgUser pkg = new PkgUser("com.android.pkg", 0);
116         DirtyTracker tracker = mock(DirtyTracker.class);
117         SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
118         Uri base = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
119                 .authority("com.android.pkg.slices").build();
120 
121         PkgUser testPkg = new PkgUser("other", 2);
122 
123         client.grantUri(base.buildUpon()
124                 .appendPath("first")
125                 .build(), testPkg);
126         client.grantUri(base.buildUpon()
127                 .appendPath("first")
128                 .appendPath("second")
129                 .build(), testPkg);
130 
131         assertTrue(client.hasPermission(base.buildUpon()
132                 .appendPath("first")
133                 .appendPath("third")
134                 .build(), testPkg.getUserId()));
135 
136         ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
137         assertEquals(1, authorities.get(0).getPaths().size());
138     }
139 
140     @Test
testAddConsolidate_addSecond()141     public void testAddConsolidate_addSecond() {
142         PkgUser pkg = new PkgUser("com.android.pkg", 0);
143         DirtyTracker tracker = mock(DirtyTracker.class);
144         SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
145         Uri base = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
146                 .authority("com.android.pkg.slices").build();
147 
148         PkgUser testPkg = new PkgUser("other", 2);
149 
150         client.grantUri(base.buildUpon()
151                 .appendPath("first")
152                 .appendPath("second")
153                 .build(), testPkg);
154         client.grantUri(base.buildUpon()
155                 .appendPath("first")
156                 .build(), testPkg);
157 
158         assertTrue(client.hasPermission(base.buildUpon()
159                 .appendPath("first")
160                 .appendPath("third")
161                 .build(), testPkg.getUserId()));
162 
163         ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
164         assertEquals(1, authorities.get(0).getPaths().size());
165     }
166 
167     @Test
testDirty_addAuthority()168     public void testDirty_addAuthority() {
169         PkgUser pkg = new PkgUser("com.android.pkg", 0);
170         DirtyTracker tracker = mock(DirtyTracker.class);
171         SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
172 
173         client.getOrCreateAuthority(new PkgUser("some_auth", 2), new PkgUser("com.pkg", 2));
174 
175         verify(tracker).onPersistableDirty(eq(client));
176     }
177 
178     @Test
testDirty_addPkg()179     public void testDirty_addPkg() {
180         PkgUser pkg = new PkgUser("com.android.pkg", 0);
181         DirtyTracker tracker = mock(DirtyTracker.class);
182         SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
183 
184         SliceAuthority auth = client.getOrCreateAuthority(
185                 new PkgUser("some_auth", 2),
186                 new PkgUser("com.pkg", 2));
187         clearInvocations(tracker);
188 
189         auth.addPath(Arrays.asList("/something/"));
190 
191         verify(tracker).onPersistableDirty(eq(client));
192     }
193 
194     @Test
testCreation()195     public void testCreation() {
196         SliceClientPermissions client = createClient();
197         ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
198         authorities.sort(Comparator.comparing(SliceAuthority::getAuthority));
199 
200         assertEquals(2, authorities.size());
201         assertEquals("com.android.pkg", authorities.get(0).getAuthority());
202         assertEquals("com.android.pkg.slices", authorities.get(1).getAuthority());
203 
204         assertEquals(1, authorities.get(0).getPaths().size());
205         assertEquals(2, authorities.get(1).getPaths().size());
206     }
207 
208     @Test
testSerialization()209     public void testSerialization() throws XmlPullParserException, IOException {
210         SliceClientPermissions client = createClient();
211         client.setHasFullAccess(true);
212 
213         ByteArrayOutputStream output = new ByteArrayOutputStream();
214         XmlSerializer serializer = XmlPullParserFactory.newInstance().newSerializer();
215         serializer.setOutput(output, Encoding.UTF_8.name());
216 
217         client.writeTo(serializer);
218         serializer.flush();
219 
220         ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
221         XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
222         parser.setInput(input, Encoding.UTF_8.name());
223 
224         SliceClientPermissions deser = SliceClientPermissions.createFrom(parser,
225                 mock(DirtyTracker.class));
226 
227         assertEquivalent(client, deser);
228     }
229 
230     @Test(expected = XmlPullParserException.class)
testReadEmptyFile_ThrowException()231     public void testReadEmptyFile_ThrowException() throws XmlPullParserException, IOException {
232         ByteArrayOutputStream output = new ByteArrayOutputStream();
233         XmlSerializer serializer = XmlPullParserFactory.newInstance().newSerializer();
234         serializer.setOutput(output, Encoding.UTF_8.name());
235         // create empty xml document
236         serializer.startDocument(null, true);
237         serializer.endDocument();
238         serializer.flush();
239 
240         ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
241         XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
242         parser.setInput(input, Encoding.UTF_8.name());
243         SliceClientPermissions.createFrom(parser, mock(DirtyTracker.class));
244         // Should throw exception since the xml is empty
245     }
246 
assertEquivalent(SliceClientPermissions o1, SliceClientPermissions o2)247     private void assertEquivalent(SliceClientPermissions o1, SliceClientPermissions o2) {
248         assertEquals(o1.getPkg(), o2.getPkg());
249         ArrayList<SliceAuthority> a1 = new ArrayList<>(o1.getAuthorities());
250         ArrayList<SliceAuthority> a2 = new ArrayList<>(o2.getAuthorities());
251         a1.sort(Comparator.comparing(SliceAuthority::getAuthority));
252         a2.sort(Comparator.comparing(SliceAuthority::getAuthority));
253         assertEquals(a1, a2);
254     }
255 
createClient()256     private static SliceClientPermissions createClient() {
257         PkgUser pkg = new PkgUser("com.android.pkg", 2);
258         DirtyTracker tracker = mock(DirtyTracker.class);
259         SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
260 
261         SliceAuthority auth = client.getOrCreateAuthority(
262                 new PkgUser("com.android.pkg.slices", 3),
263                 new PkgUser("com.android.pkg", 3));
264         auth.addPath(Arrays.asList("/something/"));
265         auth.addPath(Arrays.asList("/something/else"));
266 
267         auth = client.getOrCreateAuthority(
268                 new PkgUser("com.android.pkg", 3),
269                 new PkgUser("com.pkg", 1));
270         auth.addPath(Arrays.asList("/somewhere"));
271         return client;
272     }
273 
274 }