blob: cd39144d5c6bdeb6fdafd134f1630001735d2140 [file] [log] [blame]
Sudheer Shankaae53d112020-01-31 14:20:53 -08001/*
2 * Copyright 2020 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 */
16package com.android.server.blob;
17
18import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
19import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
20import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
21import static com.android.server.blob.BlobStoreConfig.SESSION_EXPIRY_TIMEOUT_MILLIS;
22
23import static com.google.common.truth.Truth.assertThat;
24
25import static org.mockito.Mockito.never;
26import static org.mockito.Mockito.verify;
27
28import android.app.blob.BlobHandle;
29import android.content.Context;
30import android.os.Handler;
31import android.os.Looper;
32import android.os.Message;
33import android.os.UserHandle;
34import android.platform.test.annotations.Presubmit;
35import android.util.ArrayMap;
36import android.util.LongSparseArray;
37
38import androidx.test.InstrumentationRegistry;
39import androidx.test.filters.SmallTest;
40import androidx.test.runner.AndroidJUnit4;
41
42import com.android.server.blob.BlobStoreManagerService.Injector;
43
44import org.junit.After;
45import org.junit.Before;
46import org.junit.Test;
47import org.junit.runner.RunWith;
48import org.mockito.Mock;
49import org.mockito.MockitoSession;
50import org.mockito.quality.Strictness;
51
52import java.io.File;
53
54@RunWith(AndroidJUnit4.class)
55@SmallTest
56@Presubmit
57public class BlobStoreManagerServiceTest {
58 private Context mContext;
59 private Handler mHandler;
60 private BlobStoreManagerService mService;
61
62 private MockitoSession mMockitoSession;
63
64 @Mock
65 private File mBlobsDir;
66
67 private LongSparseArray<BlobStoreSession> mUserSessions;
68 private ArrayMap<BlobHandle, BlobMetadata> mUserBlobs;
69
70 private static final String TEST_PKG1 = "com.example1";
71 private static final String TEST_PKG2 = "com.example2";
72 private static final String TEST_PKG3 = "com.example3";
73
74 private static final int TEST_UID1 = 10001;
75 private static final int TEST_UID2 = 10002;
76 private static final int TEST_UID3 = 10003;
77
78 @Before
79 public void setUp() {
80 // Share classloader to allow package private access.
81 System.setProperty("dexmaker.share_classloader", "true");
82
83 mMockitoSession = mockitoSession()
84 .initMocks(this)
85 .strictness(Strictness.LENIENT)
86 .mockStatic(BlobStoreConfig.class)
87 .startMocking();
88
89 doReturn(mBlobsDir).when(() -> BlobStoreConfig.getBlobsDir());
90 doReturn(true).when(mBlobsDir).exists();
91 doReturn(new File[0]).when(mBlobsDir).listFiles();
92
93 mContext = InstrumentationRegistry.getTargetContext();
94 mHandler = new TestHandler(Looper.getMainLooper());
95 mService = new BlobStoreManagerService(mContext, new TestInjector());
96 mUserSessions = new LongSparseArray<>();
97 mUserBlobs = new ArrayMap<>();
98
99 mService.addUserSessionsForTest(mUserSessions, UserHandle.myUserId());
100 mService.addUserBlobsForTest(mUserBlobs, UserHandle.myUserId());
101 }
102
103 @After
104 public void tearDown() {
105 if (mMockitoSession != null) {
106 mMockitoSession.finishMocking();
107 }
108 }
109
110 @Test
111 public void testHandlePackageRemoved() throws Exception {
112 // Setup sessions
113 final File sessionFile1 = mock(File.class);
114 final long sessionId1 = 11;
115 final BlobStoreSession session1 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1,
116 sessionId1, sessionFile1);
117 mUserSessions.append(sessionId1, session1);
118
119 final File sessionFile2 = mock(File.class);
120 final long sessionId2 = 25;
121 final BlobStoreSession session2 = createBlobStoreSessionMock(TEST_PKG2, TEST_UID2,
122 sessionId2, sessionFile2);
123 mUserSessions.append(sessionId2, session2);
124
125 final File sessionFile3 = mock(File.class);
126 final long sessionId3 = 37;
127 final BlobStoreSession session3 = createBlobStoreSessionMock(TEST_PKG3, TEST_UID3,
128 sessionId3, sessionFile3);
129 mUserSessions.append(sessionId3, session3);
130
131 final File sessionFile4 = mock(File.class);
132 final long sessionId4 = 48;
133 final BlobStoreSession session4 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1,
134 sessionId4, sessionFile4);
135 mUserSessions.append(sessionId4, session4);
136
137 // Setup blobs
138 final long blobId1 = 978;
139 final File blobFile1 = mock(File.class);
140 final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(),
141 "label1", System.currentTimeMillis(), "tag1");
142 final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobId1, blobFile1, true);
143 mUserBlobs.put(blobHandle1, blobMetadata1);
144
145 final long blobId2 = 347;
146 final File blobFile2 = mock(File.class);
147 final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(),
148 "label2", System.currentTimeMillis(), "tag2");
149 final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2, false);
150 mUserBlobs.put(blobHandle2, blobMetadata2);
151
Sudheer Shanka60803032020-02-13 12:47:59 -0800152 mService.addActiveIdsForTest(sessionId1, sessionId2, sessionId3, sessionId4,
Sudheer Shankaae53d112020-01-31 14:20:53 -0800153 blobId1, blobId2);
154
155 // Invoke test method
156 mService.handlePackageRemoved(TEST_PKG1, TEST_UID1);
157
158 // Verify sessions are removed
159 verify(sessionFile1).delete();
160 verify(sessionFile2, never()).delete();
161 verify(sessionFile3, never()).delete();
162 verify(sessionFile4).delete();
163
164 assertThat(mUserSessions.size()).isEqualTo(2);
165 assertThat(mUserSessions.get(sessionId1)).isNull();
166 assertThat(mUserSessions.get(sessionId2)).isNotNull();
167 assertThat(mUserSessions.get(sessionId3)).isNotNull();
168 assertThat(mUserSessions.get(sessionId4)).isNull();
169
170 // Verify blobs are removed
171 verify(blobMetadata1).removeCommitter(TEST_PKG1, TEST_UID1);
172 verify(blobMetadata1).removeLeasee(TEST_PKG1, TEST_UID1);
173 verify(blobMetadata2).removeCommitter(TEST_PKG1, TEST_UID1);
174 verify(blobMetadata2).removeLeasee(TEST_PKG1, TEST_UID1);
175
176 verify(blobFile1, never()).delete();
177 verify(blobFile2).delete();
178
179 assertThat(mUserBlobs.size()).isEqualTo(1);
180 assertThat(mUserBlobs.get(blobHandle1)).isNotNull();
181 assertThat(mUserBlobs.get(blobHandle2)).isNull();
182
Sudheer Shanka60803032020-02-13 12:47:59 -0800183 assertThat(mService.getActiveIdsForTest()).containsExactly(
Sudheer Shankaae53d112020-01-31 14:20:53 -0800184 sessionId2, sessionId3, blobId1);
Sudheer Shanka60803032020-02-13 12:47:59 -0800185 assertThat(mService.getKnownIdsForTest()).containsExactly(
186 sessionId1, sessionId2, sessionId3, sessionId4, blobId1, blobId2);
Sudheer Shankaae53d112020-01-31 14:20:53 -0800187 }
188
189 @Test
190 public void testHandleIdleMaintenance_deleteUnknownBlobs() throws Exception {
191 // Setup blob files
192 final long testId1 = 286;
193 final File file1 = mock(File.class);
194 doReturn(String.valueOf(testId1)).when(file1).getName();
195 final long testId2 = 349;
196 final File file2 = mock(File.class);
197 doReturn(String.valueOf(testId2)).when(file2).getName();
198 final long testId3 = 7355;
199 final File file3 = mock(File.class);
200 doReturn(String.valueOf(testId3)).when(file3).getName();
201
202 doReturn(new File[] {file1, file2, file3}).when(mBlobsDir).listFiles();
Sudheer Shanka60803032020-02-13 12:47:59 -0800203 mService.addActiveIdsForTest(testId1, testId3);
Sudheer Shankaae53d112020-01-31 14:20:53 -0800204
205 // Invoke test method
206 mService.handleIdleMaintenanceLocked();
207
Sudheer Shanka60803032020-02-13 12:47:59 -0800208 // Verify unknown blobs are deleted
Sudheer Shankaae53d112020-01-31 14:20:53 -0800209 verify(file1, never()).delete();
210 verify(file2).delete();
211 verify(file3, never()).delete();
212 }
213
Sudheer Shankaae53d112020-01-31 14:20:53 -0800214 @Test
215 public void testHandleIdleMaintenance_deleteStaleSessions() throws Exception {
216 // Setup sessions
217 final File sessionFile1 = mock(File.class);
218 doReturn(System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS + 1000)
219 .when(sessionFile1).lastModified();
220 final long sessionId1 = 342;
Sudheer Shanka7c0111a2020-02-07 11:38:36 -0800221 final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(),
222 "label1", System.currentTimeMillis() - 1000, "tag1");
Sudheer Shankaae53d112020-01-31 14:20:53 -0800223 final BlobStoreSession session1 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1,
224 sessionId1, sessionFile1, blobHandle1);
225 mUserSessions.append(sessionId1, session1);
226
227 final File sessionFile2 = mock(File.class);
228 doReturn(System.currentTimeMillis() - 20000)
229 .when(sessionFile2).lastModified();
230 final long sessionId2 = 4597;
Sudheer Shanka7c0111a2020-02-07 11:38:36 -0800231 final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(),
232 "label2", System.currentTimeMillis() + 20000, "tag2");
Sudheer Shankaae53d112020-01-31 14:20:53 -0800233 final BlobStoreSession session2 = createBlobStoreSessionMock(TEST_PKG2, TEST_UID2,
234 sessionId2, sessionFile2, blobHandle2);
235 mUserSessions.append(sessionId2, session2);
236
237 final File sessionFile3 = mock(File.class);
238 doReturn(System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS - 2000)
239 .when(sessionFile3).lastModified();
240 final long sessionId3 = 9484;
Sudheer Shanka7c0111a2020-02-07 11:38:36 -0800241 final BlobHandle blobHandle3 = BlobHandle.createWithSha256("digest3".getBytes(),
242 "label3", System.currentTimeMillis() + 30000, "tag3");
Sudheer Shankaae53d112020-01-31 14:20:53 -0800243 final BlobStoreSession session3 = createBlobStoreSessionMock(TEST_PKG3, TEST_UID3,
244 sessionId3, sessionFile3, blobHandle3);
245 mUserSessions.append(sessionId3, session3);
246
Sudheer Shanka60803032020-02-13 12:47:59 -0800247 mService.addActiveIdsForTest(sessionId1, sessionId2, sessionId3);
Sudheer Shankaae53d112020-01-31 14:20:53 -0800248
249 // Invoke test method
250 mService.handleIdleMaintenanceLocked();
251
252 // Verify stale sessions are removed
253 verify(sessionFile1).delete();
254 verify(sessionFile2, never()).delete();
255 verify(sessionFile3).delete();
256
257 assertThat(mUserSessions.size()).isEqualTo(1);
258 assertThat(mUserSessions.get(sessionId2)).isNotNull();
259
Sudheer Shanka60803032020-02-13 12:47:59 -0800260 assertThat(mService.getActiveIdsForTest()).containsExactly(sessionId2);
261 assertThat(mService.getKnownIdsForTest()).containsExactly(
262 sessionId1, sessionId2, sessionId3);
Sudheer Shankaae53d112020-01-31 14:20:53 -0800263 }
264
265 @Test
266 public void testHandleIdleMaintenance_deleteStaleBlobs() throws Exception {
267 // Setup blobs
268 final long blobId1 = 3489;
269 final File blobFile1 = mock(File.class);
270 final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(),
271 "label1", System.currentTimeMillis() - 2000, "tag1");
272 final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobId1, blobFile1, true);
273 mUserBlobs.put(blobHandle1, blobMetadata1);
274
275 final long blobId2 = 78974;
276 final File blobFile2 = mock(File.class);
277 final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(),
278 "label2", System.currentTimeMillis() + 30000, "tag2");
279 final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2, true);
280 mUserBlobs.put(blobHandle2, blobMetadata2);
281
282 final long blobId3 = 97;
283 final File blobFile3 = mock(File.class);
284 final BlobHandle blobHandle3 = BlobHandle.createWithSha256("digest3".getBytes(),
285 "label3", System.currentTimeMillis() + 4400000, "tag3");
286 final BlobMetadata blobMetadata3 = createBlobMetadataMock(blobId3, blobFile3, false);
287 mUserBlobs.put(blobHandle3, blobMetadata3);
288
Sudheer Shanka60803032020-02-13 12:47:59 -0800289 mService.addActiveIdsForTest(blobId1, blobId2, blobId3);
Sudheer Shankaae53d112020-01-31 14:20:53 -0800290
291 // Invoke test method
292 mService.handleIdleMaintenanceLocked();
293
294 // Verify stale blobs are removed
295 verify(blobFile1).delete();
296 verify(blobFile2, never()).delete();
297 verify(blobFile3).delete();
298
299 assertThat(mUserBlobs.size()).isEqualTo(1);
300 assertThat(mUserBlobs.get(blobHandle2)).isNotNull();
301
Sudheer Shanka60803032020-02-13 12:47:59 -0800302 assertThat(mService.getActiveIdsForTest()).containsExactly(blobId2);
303 assertThat(mService.getKnownIdsForTest()).containsExactly(blobId1, blobId2, blobId3);
Sudheer Shankaae53d112020-01-31 14:20:53 -0800304 }
305
Sudheer Shanka364364b2020-02-19 17:56:09 -0800306 @Test
307 public void testGetTotalUsageBytes() throws Exception {
308 // Setup blobs
309 final BlobMetadata blobMetadata1 = mock(BlobMetadata.class);
310 final long size1 = 4567;
311 doReturn(size1).when(blobMetadata1).getSize();
312 doReturn(true).when(blobMetadata1).isALeasee(TEST_PKG1, TEST_UID1);
313 doReturn(true).when(blobMetadata1).isALeasee(TEST_PKG2, TEST_UID2);
314 mUserBlobs.put(mock(BlobHandle.class), blobMetadata1);
315
316 final BlobMetadata blobMetadata2 = mock(BlobMetadata.class);
317 final long size2 = 89475;
318 doReturn(size2).when(blobMetadata2).getSize();
319 doReturn(false).when(blobMetadata2).isALeasee(TEST_PKG1, TEST_UID1);
320 doReturn(true).when(blobMetadata2).isALeasee(TEST_PKG2, TEST_UID2);
321 mUserBlobs.put(mock(BlobHandle.class), blobMetadata2);
322
323 final BlobMetadata blobMetadata3 = mock(BlobMetadata.class);
324 final long size3 = 328732;
325 doReturn(size3).when(blobMetadata3).getSize();
326 doReturn(true).when(blobMetadata3).isALeasee(TEST_PKG1, TEST_UID1);
327 doReturn(false).when(blobMetadata3).isALeasee(TEST_PKG2, TEST_UID2);
328 mUserBlobs.put(mock(BlobHandle.class), blobMetadata3);
329
330 // Verify usage is calculated correctly
331 assertThat(mService.getTotalUsageBytesLocked(TEST_UID1, TEST_PKG1))
332 .isEqualTo(size1 + size3);
333 assertThat(mService.getTotalUsageBytesLocked(TEST_UID2, TEST_PKG2))
334 .isEqualTo(size1 + size2);
335 }
336
Sudheer Shankaae53d112020-01-31 14:20:53 -0800337 private BlobStoreSession createBlobStoreSessionMock(String ownerPackageName, int ownerUid,
338 long sessionId, File sessionFile) {
339 return createBlobStoreSessionMock(ownerPackageName, ownerUid, sessionId, sessionFile,
340 mock(BlobHandle.class));
341 }
342 private BlobStoreSession createBlobStoreSessionMock(String ownerPackageName, int ownerUid,
343 long sessionId, File sessionFile, BlobHandle blobHandle) {
344 final BlobStoreSession session = mock(BlobStoreSession.class);
345 doReturn(ownerPackageName).when(session).getOwnerPackageName();
346 doReturn(ownerUid).when(session).getOwnerUid();
347 doReturn(sessionId).when(session).getSessionId();
348 doReturn(sessionFile).when(session).getSessionFile();
349 doReturn(blobHandle).when(session).getBlobHandle();
350 return session;
351 }
352
353 private BlobMetadata createBlobMetadataMock(long blobId, File blobFile, boolean hasLeases) {
354 final BlobMetadata blobMetadata = mock(BlobMetadata.class);
355 doReturn(blobId).when(blobMetadata).getBlobId();
356 doReturn(blobFile).when(blobMetadata).getBlobFile();
357 doReturn(hasLeases).when(blobMetadata).hasLeases();
358 return blobMetadata;
359 }
360
361 private class TestHandler extends Handler {
362 TestHandler(Looper looper) {
363 super(looper);
364 }
365
366 @Override
367 public void dispatchMessage(Message msg) {
368 // Ignore all messages
369 }
370 }
371
372 private class TestInjector extends Injector {
373 @Override
374 public Handler initializeMessageHandler() {
375 return mHandler;
376 }
Sudheer Shankab76f7662020-02-27 15:17:43 -0800377
378 @Override
379 public Handler getBackgroundHandler() {
380 return mHandler;
381 }
Sudheer Shankaae53d112020-01-31 14:20:53 -0800382 }
383}