1 /*
2  * Copyright (C) 2023 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 package android.ondevicepersonalization.test.scenario.ondevicepersonalization;
17 
18 import android.os.SystemClock;
19 
20 import androidx.test.platform.app.InstrumentationRegistry;
21 import androidx.test.uiautomator.UiDevice;
22 
23 import org.junit.Assert;
24 
25 import java.io.IOException;
26 import java.util.List;
27 
28 /** Helper class for interacting with download flow. */
29 public class DownloadHelper {
30     private static UiDevice sUiDevice;
31     private static final String MDD_WIFI_CHARGING_PERIODIC_TASK_JOB_ID = "1003";
32     private static final String DOWNLOAD_PROCESSING_TASK_JOB_ID = "1004";
33     private static final String MAINTENANCE_TASK_JOB_ID = "1005";
34     private static final String DOWNLOAD_JOB_SUCCESS_LOG =
35             "MddJobService: MddJobService.MddHandleTask succeeded!";
36     private static final String FILTER_AND_STORE_DATA_SUCCESS_LOG =
37             "DownloadFlow: "
38                     + "filter and store data completed, transaction successful: true";
39     private static final String FILTER_AND_STORE_NOT_NEW_LOG =
40             "DownloadFlow: syncToken is not newer than existing token.";
41     private static final long DOWNLOAD_JOB_COMPLETION_TIMEOUT = 120_000;
42     private static final long DOWNLOAD_PROCESSING_JOB_COMPLETION_TIMEOUT = 120_000;
43 
44     /** Commands to prepare the device and odp module before testing. */
initialize()45     public static void initialize() throws IOException {
46         executeShellCommand(
47                 "device_config set_sync_disabled_for_tests persistent");
48         executeShellCommand(
49                 "device_config put on_device_personalization global_kill_switch false");
50         executeShellCommand(
51                 "device_config put on_device_personalization "
52                     + "enable_ondevicepersonalization_apis true");
53         executeShellCommand(
54                 "device_config put on_device_personalization "
55                     + "enable_personalization_status_override true");
56         executeShellCommand(
57                 "device_config put on_device_personalization "
58                     + "personalization_status_override_value true");
59         executeShellCommand("setprop log.tag.ondevicepersonalization VERBOSE");
60         executeShellCommand(
61                 "am broadcast -a android.intent.action.BOOT_COMPLETED -p "
62                     + "com.google.android.ondevicepersonalization.services");
63         executeShellCommand(
64                 "cmd jobscheduler run -f "
65                     + "com.google.android.ondevicepersonalization.services 1000");
66         SystemClock.sleep(5000);
67         executeShellCommand(
68                 "cmd jobscheduler run -f "
69                     + "com.google.android.ondevicepersonalization.services 1006");
70         SystemClock.sleep(5000);
71     }
72 
73     /** Kill running processes to get performance measurement under cold start */
killRunningProcess()74     public static void killRunningProcess() throws IOException {
75         executeShellCommand("am kill com.google.android.ondevicepersonalization.services");
76         executeShellCommand("am kill com.google.android.ondevicepersonalization.services:"
77                 + "com.android.ondevicepersonalization."
78                 + "libraries.plugin.internal.PluginExecutorService");
79         executeShellCommand("am kill com.google.android.ondevicepersonalization.services:"
80                 + "plugin_disable_art_image_:"
81                 + "com.android.ondevicepersonalization."
82                 + "libraries.plugin.internal.PluginExecutorService");
83         SystemClock.sleep(2000);
84     }
pressHome()85     public static void pressHome() {
86         getUiDevice().pressHome();
87     }
88 
cleanupDatabase()89     public void cleanupDatabase() throws IOException {
90         executeShellCommand(
91                 "cmd jobscheduler run -f com.google.android.ondevicepersonalization.services "
92                         + MAINTENANCE_TASK_JOB_ID);
93         SystemClock.sleep(5000);
94     }
95 
cleanupDownloadedMetadata()96     public void cleanupDownloadedMetadata() throws IOException {
97         executeShellCommand(
98                 "cmd jobscheduler run -f com.google.android.ondevicepersonalization.services "
99                         + MDD_WIFI_CHARGING_PERIODIC_TASK_JOB_ID);
100         SystemClock.sleep(5000);
101     }
102 
downloadVendorData()103     public void downloadVendorData() throws IOException {
104         executeShellCommand("logcat -c"); // Cleans the log buffer
105         executeShellCommand("logcat -G 32M"); // Set log buffer to 32MB
106         executeShellCommand(
107                 "cmd jobscheduler run -f com.google.android.ondevicepersonalization.services "
108                         + MDD_WIFI_CHARGING_PERIODIC_TASK_JOB_ID);
109 
110         boolean foundDownloadJobSuccessLog = findLog(
111                 DOWNLOAD_JOB_SUCCESS_LOG,
112                 DOWNLOAD_JOB_COMPLETION_TIMEOUT,
113                 5000);
114 
115         if (!foundDownloadJobSuccessLog) {
116             Assert.fail(String.format(
117                     "Failed to find download job success log %s within test window %d ms",
118                     DOWNLOAD_JOB_SUCCESS_LOG,
119                     DOWNLOAD_JOB_COMPLETION_TIMEOUT));
120         }
121     }
122 
123     /**
124      * Process downloaded vendor data assuming it is new data.
125      */
processDownloadedVendorData()126     public void processDownloadedVendorData() throws IOException {
127         processDownloadVendorData(true);
128     }
129 
130     /**
131      * Process downloaded vendor data, ignoring whether it is new or existing downloaded data.
132      */
processExistingOrNewDownloadedVendorData()133     public void processExistingOrNewDownloadedVendorData() throws IOException {
134         processDownloadVendorData(false);
135     }
136 
processDownloadVendorData(boolean newDownload)137     private void processDownloadVendorData(boolean newDownload) throws IOException {
138         executeShellCommand(
139                 "cmd jobscheduler run -f com.google.android.ondevicepersonalization.services "
140                         + DOWNLOAD_PROCESSING_TASK_JOB_ID);
141 
142         if (newDownload) {
143             boolean foundFilterAndStoreDataSuccessLog = findLog(
144                     FILTER_AND_STORE_DATA_SUCCESS_LOG,
145                     DOWNLOAD_PROCESSING_JOB_COMPLETION_TIMEOUT,
146                     5000);
147 
148             if (!foundFilterAndStoreDataSuccessLog) {
149                 Assert.fail(String.format(
150                         "Failed to find filter and store data success log %s within test window "
151                                 + "%d ms",
152                         foundFilterAndStoreDataSuccessLog,
153                         DOWNLOAD_PROCESSING_JOB_COMPLETION_TIMEOUT));
154             }
155         } else {
156             boolean foundFilterAndStoreDataSuccessLog = findLog(
157                     List.of(FILTER_AND_STORE_DATA_SUCCESS_LOG, FILTER_AND_STORE_NOT_NEW_LOG),
158                     DOWNLOAD_PROCESSING_JOB_COMPLETION_TIMEOUT,
159                     5000);
160 
161             if (!foundFilterAndStoreDataSuccessLog) {
162                 Assert.fail(String.format(
163                         "Failed to find filter and store data success log %s within test window "
164                                 + "%d ms",
165                         foundFilterAndStoreDataSuccessLog,
166                         DOWNLOAD_PROCESSING_JOB_COMPLETION_TIMEOUT));
167             }
168         }
169     }
170 
171     /** Attempt to find a specific log entry within the timeout window */
findLog(final String targetLog, long timeoutMillis, long queryIntervalMillis)172     private boolean findLog(final String targetLog, long timeoutMillis,
173                             long queryIntervalMillis) throws IOException {
174         return findLog(List.of(targetLog), timeoutMillis, queryIntervalMillis);
175     }
176 
177     /** Attempt to find a one of specific log entries within the timeout window */
findLog(final List<String> targetLogs, long timeoutMillis, long queryIntervalMillis)178     private boolean findLog(final List<String> targetLogs, long timeoutMillis,
179             long queryIntervalMillis) throws IOException {
180 
181         long startTime = System.currentTimeMillis();
182         while (System.currentTimeMillis() - startTime < timeoutMillis) {
183             String logcat = getUiDevice().executeShellCommand("logcat -d");
184             if (targetLogs.stream().anyMatch(logcat::contains)) {
185                 return true;
186             }
187             SystemClock.sleep(queryIntervalMillis);
188         }
189         return false;
190     }
191 
executeShellCommand(String cmd)192     private static void executeShellCommand(String cmd) {
193         try {
194             getUiDevice().executeShellCommand(cmd);
195         } catch (IOException e) {
196             Assert.fail("Failed to execute shell command: " + cmd + ". error: " + e);
197         }
198     }
199 
getUiDevice()200     private static UiDevice getUiDevice() {
201         if (sUiDevice == null) {
202             sUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
203         }
204         return sUiDevice;
205     }
206 
207     /** Commands to return device to original state */
wrapUp()208     public static void wrapUp() throws IOException {
209         executeShellCommand(
210                 "device_config set_sync_disabled_for_tests none");
211     }
212 
213 }
214