• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ondevicepersonalization.services.download;
18 
19 import static com.android.ondevicepersonalization.services.FlagsConstants.KEY_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED;
20 
21 import static org.junit.Assert.assertArrayEquals;
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 
26 import android.adservices.ondevicepersonalization.DownloadCompletedOutputParcel;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.database.Cursor;
30 
31 import androidx.test.core.app.ApplicationProvider;
32 
33 import com.android.compatibility.common.util.ShellUtils;
34 import com.android.dx.mockito.inline.extended.ExtendedMockito;
35 import com.android.modules.utils.build.SdkLevel;
36 import com.android.modules.utils.testing.ExtendedMockitoRule;
37 import com.android.odp.module.common.PackageUtils;
38 import com.android.ondevicepersonalization.services.Flags;
39 import com.android.ondevicepersonalization.services.FlagsFactory;
40 import com.android.ondevicepersonalization.services.PhFlagsTestUtil;
41 import com.android.ondevicepersonalization.services.StableFlags;
42 import com.android.ondevicepersonalization.services.data.OnDevicePersonalizationDbHelper;
43 import com.android.ondevicepersonalization.services.data.vendor.OnDevicePersonalizationVendorDataDao;
44 import com.android.ondevicepersonalization.services.data.vendor.VendorData;
45 import com.android.ondevicepersonalization.services.data.vendor.VendorDataContract;
46 import com.android.ondevicepersonalization.services.download.mdd.MobileDataDownloadFactory;
47 import com.android.ondevicepersonalization.services.download.mdd.OnDevicePersonalizationFileGroupPopulator;
48 
49 import com.google.android.libraries.mobiledatadownload.DownloadFileGroupRequest;
50 import com.google.android.libraries.mobiledatadownload.MobileDataDownload;
51 import com.google.android.libraries.mobiledatadownload.RemoveFileGroupsByFilterRequest;
52 import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage;
53 import com.google.common.util.concurrent.FutureCallback;
54 import com.google.common.util.concurrent.ListeningExecutorService;
55 import com.google.common.util.concurrent.MoreExecutors;
56 import com.google.common.util.concurrent.SettableFuture;
57 
58 import org.junit.After;
59 import org.junit.Before;
60 import org.junit.Rule;
61 import org.junit.Test;
62 import org.junit.runner.RunWith;
63 import org.junit.runners.JUnit4;
64 import org.mockito.quality.Strictness;
65 
66 import java.util.ArrayList;
67 import java.util.Base64;
68 import java.util.List;
69 import java.util.concurrent.CountDownLatch;
70 
71 @RunWith(JUnit4.class)
72 public class OnDevicePersonalizationDataProcessingAsyncCallableTests {
73     private final Context mContext = ApplicationProvider.getApplicationContext();
74     private OnDevicePersonalizationFileGroupPopulator mPopulator;
75     private MobileDataDownload mMdd;
76     private String mPackageName;
77     private SynchronousFileStorage mFileStorage;
78     private final VendorData mContent1 = new VendorData.Builder()
79             .setKey("key1")
80             .setData("dGVzdGRhdGEx".getBytes())
81             .build();
82 
83     private final VendorData mContent2 = new VendorData.Builder()
84             .setKey("key2")
85             .setData("dGVzdGRhdGEy".getBytes())
86             .build();
87 
88     private final VendorData mContentExtra = new VendorData.Builder()
89             .setKey("keyExtra")
90             .setData("extra".getBytes())
91             .build();
92 
93     private ComponentName mService;
94     private FutureCallback mTestCallback;
95     private boolean mCallbackSuccess;
96     private boolean mCallbackFailure;
97     private CountDownLatch mLatch;
98 
99     private Flags mSpyFlags = new Flags() {
100         int mIsolatedServiceDeadlineSeconds = 30;
101         @Override public int getIsolatedServiceDeadlineSeconds() {
102             return mIsolatedServiceDeadlineSeconds;
103         }
104     };
105 
106     @Rule
107     public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
108             .mockStatic(FlagsFactory.class)
109             .spyStatic(StableFlags.class)
110             .setStrictness(Strictness.LENIENT)
111             .build();
112 
113     @Before
setup()114     public void setup() throws Exception {
115         mPackageName = mContext.getPackageName();
116         mService = ComponentName.createRelative(
117                 mPackageName, "com.test.TestPersonalizationService");
118         mFileStorage = MobileDataDownloadFactory.getFileStorage(mContext);
119         // Use direct executor to keep all work sequential for the tests
120         ListeningExecutorService executorService = MoreExecutors.newDirectExecutorService();
121         mMdd = MobileDataDownloadFactory.getMdd(mContext, executorService, executorService);
122         mPopulator = new OnDevicePersonalizationFileGroupPopulator(mContext);
123         RemoveFileGroupsByFilterRequest request =
124                 RemoveFileGroupsByFilterRequest.newBuilder().build();
125         MobileDataDownloadFactory.getMdd(mContext).removeFileGroupsByFilter(request).get();
126 
127         // Initialize the DB as a test instance
128         OnDevicePersonalizationVendorDataDao.getInstanceForTest(mContext, mService,
129                 PackageUtils.getCertDigest(mContext, mPackageName));
130 
131         PhFlagsTestUtil.setUpDeviceConfigPermissions();
132         ExtendedMockito.doReturn(mSpyFlags).when(FlagsFactory::getFlags);
133         ExtendedMockito.doReturn(SdkLevel.isAtLeastU()).when(
134                 () -> StableFlags.get(KEY_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED));
135         ShellUtils.runShellCommand("settings put global hidden_api_policy 1");
136 
137         mLatch = new CountDownLatch(1);
138         mTestCallback = new FutureCallback<DownloadCompletedOutputParcel>() {
139             @Override
140             public void onSuccess(DownloadCompletedOutputParcel result) {
141                 mCallbackSuccess = true;
142                 mLatch.countDown();
143             }
144 
145             @Override
146             public void onFailure(Throwable t) {
147                 mCallbackFailure = true;
148                 mLatch.countDown();
149             }
150         };
151     }
152 
153     @Test
testRun()154     public void testRun() throws Exception {
155         OnDevicePersonalizationVendorDataDao dao =
156                 OnDevicePersonalizationVendorDataDao.getInstanceForTest(mContext, mService,
157                         PackageUtils.getCertDigest(mContext, mPackageName));
158         var originalIsolatedServiceAllowList =
159                 FlagsFactory.getFlags().getIsolatedServiceAllowList();
160         PhFlagsTestUtil.setIsolatedServiceAllowList(
161                 "com.android.ondevicepersonalization.servicetests");
162         mPopulator.refreshFileGroups(mMdd).get();
163         PhFlagsTestUtil.setIsolatedServiceAllowList(originalIsolatedServiceAllowList);
164         String fileGroupName = OnDevicePersonalizationFileGroupPopulator.createPackageFileGroupName(
165                 mPackageName, mContext);
166         // Trigger the download immediately.
167         mMdd.downloadFileGroup(
168                 DownloadFileGroupRequest.newBuilder().setGroupName(fileGroupName).build()).get();
169 
170         List<VendorData> existingData = new ArrayList<>();
171         existingData.add(mContentExtra);
172         List<String> retain = new ArrayList<>();
173         retain.add("keyExtra");
174         assertTrue(dao.batchUpdateOrInsertVendorDataTransaction(existingData, retain,
175                 100));
176 
177         OnDevicePersonalizationDataProcessingAsyncCallable callable =
178                 new OnDevicePersonalizationDataProcessingAsyncCallable(
179                         mPackageName, mContext, new TestInjector());
180 
181         callable.call();
182         mLatch.await();
183 
184         Cursor cursor = dao.readAllVendorData();
185         List<VendorData> vendorDataList = new ArrayList<>();
186         while (cursor.moveToNext()) {
187             String key = cursor.getString(
188                     cursor.getColumnIndexOrThrow(VendorDataContract.VendorDataEntry.KEY));
189 
190             byte[] data = cursor.getBlob(
191                     cursor.getColumnIndexOrThrow(VendorDataContract.VendorDataEntry.DATA));
192 
193             vendorDataList.add(new VendorData.Builder()
194                     .setKey(key)
195                     .setData(data)
196                     .build());
197         }
198         cursor.close();
199         assertEquals(3, vendorDataList.size());
200         for (VendorData data : vendorDataList) {
201             if (data.getKey().equals(mContent1.getKey())) {
202                 compareDataContent(mContent1, data, false);
203             } else if (data.getKey().equals(mContent2.getKey())) {
204                 compareDataContent(mContent2, data, true);
205             } else if (data.getKey().equals(mContentExtra.getKey())) {
206                 compareDataContent(mContentExtra, data, false);
207             } else {
208                 fail("Vendor data from DB contains unexpected key");
209             }
210         }
211     }
212 
213     @Test
testRunOldDataDownloaded()214     public void testRunOldDataDownloaded() throws Exception {
215         OnDevicePersonalizationVendorDataDao dao =
216                 OnDevicePersonalizationVendorDataDao.getInstanceForTest(mContext, mService,
217                         PackageUtils.getCertDigest(mContext, mPackageName));
218         var originalIsolatedServiceAllowList =
219                 FlagsFactory.getFlags().getIsolatedServiceAllowList();
220         PhFlagsTestUtil.setIsolatedServiceAllowList(
221                 "com.android.ondevicepersonalization.servicetests");
222         mPopulator.refreshFileGroups(mMdd).get();
223         PhFlagsTestUtil.setIsolatedServiceAllowList(originalIsolatedServiceAllowList);
224         String fileGroupName = OnDevicePersonalizationFileGroupPopulator.createPackageFileGroupName(
225                 mPackageName, mContext);
226         // Trigger the download immediately.
227         mMdd.downloadFileGroup(
228                 DownloadFileGroupRequest.newBuilder().setGroupName(fileGroupName).build()).get();
229 
230         List<VendorData> existingData = new ArrayList<>();
231         existingData.add(mContentExtra);
232         List<String> retain = new ArrayList<>();
233         retain.add("keyExtra");
234         assertTrue(dao.batchUpdateOrInsertVendorDataTransaction(existingData, retain,
235                 System.currentTimeMillis()));
236 
237         OnDevicePersonalizationDataProcessingAsyncCallable callable =
238                 new OnDevicePersonalizationDataProcessingAsyncCallable(
239                         mPackageName, mContext, new TestInjector());
240 
241         callable.call();
242         mLatch.await();
243 
244         Cursor cursor = dao.readAllVendorData();
245         List<VendorData> vendorDataList = new ArrayList<>();
246         while (cursor.moveToNext()) {
247             String key = cursor.getString(
248                     cursor.getColumnIndexOrThrow(VendorDataContract.VendorDataEntry.KEY));
249 
250             byte[] data = cursor.getBlob(
251                     cursor.getColumnIndexOrThrow(VendorDataContract.VendorDataEntry.DATA));
252 
253             vendorDataList.add(new VendorData.Builder()
254                     .setKey(key)
255                     .setData(data)
256                     .build());
257         }
258         cursor.close();
259         assertEquals(1, vendorDataList.size());
260         for (VendorData data : vendorDataList) {
261             if (data.getKey().equals(mContentExtra.getKey())) {
262                 compareDataContent(mContentExtra, data, false);
263             } else {
264                 fail("Vendor data from DB contains unexpected key");
265             }
266         }
267     }
268 
269     class TestInjector extends OnDevicePersonalizationDataProcessingAsyncCallable.Injector {
270         @Override
getFutureCallback( SettableFuture<Boolean> settableFuture)271         FutureCallback<DownloadCompletedOutputParcel> getFutureCallback(
272                 SettableFuture<Boolean> settableFuture) {
273             return mTestCallback;
274         }
275     }
276 
compareDataContent(VendorData expectedData, VendorData actualData, boolean base64)277     private void compareDataContent(VendorData expectedData, VendorData actualData,
278             boolean base64) {
279         assertEquals(expectedData.getKey(), actualData.getKey());
280         if (base64) {
281             assertArrayEquals(Base64.getDecoder().decode(expectedData.getData()),
282                     actualData.getData());
283         } else {
284             assertArrayEquals(expectedData.getData(), actualData.getData());
285         }
286     }
287 
288     @After
cleanup()289     public void cleanup() {
290         OnDevicePersonalizationDbHelper dbHelper =
291                 OnDevicePersonalizationDbHelper.getInstanceForTest(mContext);
292         dbHelper.getWritableDatabase().close();
293         dbHelper.getReadableDatabase().close();
294         dbHelper.close();
295     }
296 }
297