1 /*
2  * Copyright (C) 2021 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.cts.packagemanager.stats.host;
18 
19 import static com.android.cts.packagemanager.stats.host.Utils.SIGNATURE_FILE_SUFFIX;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import android.cts.statsdatom.lib.AtomTestUtils;
24 import android.cts.statsdatom.lib.ConfigUtils;
25 import android.cts.statsdatom.lib.DeviceUtils;
26 import android.cts.statsdatom.lib.ReportUtils;
27 import android.platform.test.annotations.LargeTest;
28 import android.server.ErrorSource;
29 
30 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
31 import com.android.incfs.install.IncrementalInstallSession;
32 import com.android.incfs.install.adb.ddmlib.DeviceConnection;
33 import com.android.os.AtomsProto;
34 import com.android.os.StatsLog;
35 import com.android.tradefed.build.IBuildInfo;
36 import com.android.tradefed.log.LogUtil;
37 import com.android.tradefed.testtype.DeviceTestCase;
38 import com.android.tradefed.testtype.IBuildReceiver;
39 import com.android.tradefed.util.RunUtil;
40 
41 import org.junit.After;
42 import org.junit.Before;
43 
44 import java.io.File;
45 import java.nio.file.Paths;
46 import java.util.HashMap;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.concurrent.Executors;
50 import java.util.concurrent.TimeUnit;
51 
52 public final class IncrementalAppErrorStatsTests extends DeviceTestCase implements IBuildReceiver {
53     private static final String TEST_REMOTE_DIR = "/data/local/tmp/appErrorTest";
54     private static final String HELPER_PACKAGE = "com.android.cts.packagemanager.stats.device";
55     private static final String HELPER_CLASS = ".IncrementalAppErrorStatsTestsHelper";
56     private static final String HELPER_METHOD_GET_PAGE_INDEX_TO_BLOCK = "getPageIndexToBlock";
57     private static final String HELPER_METHOD_LOAD_APKS = "loadingApks";
58     private static final String HELPER_ARG_APK_PATH = "remoteApkPath";
59     private static final String HELPER_ARG_PKG_NAME = "packageName";
60     private static final String PAGE_INDEX_TO_BLOCK = "pageIndexToBlock";
61     private static final int INSTALL_TIMEOUT_SECONDS = 10;
62     private static final int METRICS_WAIT_MILLISECONDS = 1_000;
63 
64     private IBuildInfo mCtsBuild;
65     private IncrementalInstallSession mSession;
66 
67     @Override
setBuild(IBuildInfo buildInfo)68     public void setBuild(IBuildInfo buildInfo) {
69         mCtsBuild = buildInfo;
70     }
71 
72     @Before
setUp()73     public void setUp() throws Exception {
74         if (!Utils.hasIncrementalFeature(getDevice())) {
75             return;
76         }
77         super.setUp();
78         ConfigUtils.removeConfig(getDevice());
79         ReportUtils.clearReports(getDevice());
80 
81         String remoteApkPath = Utils.pushApkToRemote(
82                 DeviceUtils.STATSD_ATOM_TEST_APK, TEST_REMOTE_DIR, mCtsBuild, getDevice());
83         final HashMap<String, String> testArgs = new HashMap<>();
84         testArgs.put(HELPER_ARG_APK_PATH, remoteApkPath);
85         Map<String, String> testResult = Utils.runDeviceTests(getDevice(), HELPER_PACKAGE,
86                 HELPER_CLASS, HELPER_METHOD_GET_PAGE_INDEX_TO_BLOCK, testArgs);
87         assertNotNull(testResult);
88         assertEquals(1, testResult.size());
89 
90         int blockedPageIndex = Integer.parseInt(testResult.get(PAGE_INDEX_TO_BLOCK));
91         assertTrue(blockedPageIndex > 0);
92         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
93         final File apk = buildHelper.getTestFile(DeviceUtils.STATSD_ATOM_TEST_APK);
94         assertNotNull(apk);
95         final File v4Signature = buildHelper.getTestFile(
96                 DeviceUtils.STATSD_ATOM_TEST_APK + SIGNATURE_FILE_SUFFIX);
97         assertNotNull(v4Signature);
98         LogUtil.CLog.i("Blocking page at index: " + blockedPageIndex);
99         mSession = new IncrementalInstallSession.Builder()
100                 .addApk(Paths.get(apk.getAbsolutePath()),
101                         Paths.get(v4Signature.getAbsolutePath()))
102                 .addExtraArgs("-g") // grant permissions
103                 .setBlockFilter(block -> {
104                     // block a page from res/raw; does not affect test run
105                     return block.getBlockIndex() != blockedPageIndex;
106                 })
107                 .build();
108         mSession.start(Executors.newCachedThreadPool(),
109                 DeviceConnection.getFactory(getDevice().getSerialNumber()));
110         mSession.waitForInstallCompleted(INSTALL_TIMEOUT_SECONDS, TimeUnit.SECONDS);
111         assertTrue(getDevice().isPackageInstalled(DeviceUtils.STATSD_ATOM_TEST_PKG));
112         // Preload most of the pages to make sure the test can run but it also causes pending reads
113         final HashMap<String, String> testArgsForLoading = new HashMap<>();
114         testArgsForLoading.put(HELPER_ARG_PKG_NAME, DeviceUtils.STATSD_ATOM_TEST_PKG);
115         Utils.runDeviceTests(getDevice(), HELPER_PACKAGE, HELPER_CLASS,
116                 HELPER_METHOD_LOAD_APKS, testArgsForLoading);
117     }
118 
hasIncrementalDeliveryV2Feature()119     private boolean hasIncrementalDeliveryV2Feature() throws Exception {
120         return "true\n".equals(getDevice().executeShellCommand(
121                 "pm has-feature android.software.incremental_delivery 2"));
122     }
123 
124     @After
tearDown()125     public void tearDown() throws Exception {
126         if (mSession != null) {
127             mSession.close();
128         }
129         getDevice().uninstallPackage(DeviceUtils.STATSD_ATOM_TEST_PKG);
130         assertFalse(getDevice().isPackageInstalled(DeviceUtils.STATSD_ATOM_TEST_PKG));
131         super.tearDown();
132     }
133 
134     @LargeTest
testAppCrashOnIncremental()135     public void testAppCrashOnIncremental() throws Exception {
136         if (!Utils.hasIncrementalFeature(getDevice())) {
137             return;
138         }
139         final int atomTag = AtomsProto.Atom.APP_CRASH_OCCURRED_FIELD_NUMBER;
140         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
141                 atomTag,  /*uidInAttributionChain=*/false);
142 
143         DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
144                 "StatsdCtsForegroundActivity", "action", "action.crash");
145         RunUtil.getDefault().sleep(METRICS_WAIT_MILLISECONDS);
146         // Sorted list of events in order in which they occurred.
147         List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
148 
149         assertThat(data).hasSize(1);
150         AtomsProto.AppCrashOccurred atom = data.get(0).getAtom().getAppCrashOccurred();
151         // UID should belong to the run activity, not any system service.
152         assertThat(atom.getUid()).isGreaterThan(10000);
153         assertThat(atom.getEventType()).isEqualTo("crash");
154         assertThat(atom.getIsInstantApp().getNumber())
155                 .isEqualTo(AtomsProto.AppCrashOccurred.InstantApp.FALSE_VALUE);
156         assertThat(atom.getForegroundState().getNumber())
157                 .isEqualTo(AtomsProto.AppCrashOccurred.ForegroundState.FOREGROUND_VALUE);
158         assertThat(atom.getPackageName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG);
159         assertThat(atom.getErrorSource()).isEqualTo(ErrorSource.DATA_APP);
160         assertTrue(atom.getIsIncremental());
161         assertFalse((1.0f - atom.getLoadingProgress()) < 0.0000001f);
162         assertTrue(atom.getMillisSinceOldestPendingRead() > 0);
163         assertEquals(3 /* HEALTH_STATUS_UNHEALTHY */, atom.getStorageHealthCode());
164         assertEquals(6 /* DATA_LOADER_IMAGE_READY */, atom.getDataLoaderStatusCode());
165         assertFalse(atom.getReadLogsEnabled());
166         assertTrue(atom.getMillisSinceLastDataLoaderBind() > 0);
167         assertEquals(0, atom.getDataLoaderBindDelayMillis());
168         if (!hasIncrementalDeliveryV2Feature()) {
169             // Skip kernel stats check if it's not supported
170             return;
171         }
172         assertTrue(atom.getTotalDelayedReads() > 0);
173         assertTrue(atom.getTotalFailedReads() > 0);
174         assertTrue(atom.getLastReadErrorMillisSince() > 0);
175         assertEquals(-62 /* -ETIME */, atom.getLastReadErrorCode());
176         assertTrue(atom.getTotalDelayedReadsDurationMillis() > 0);
177     }
178 
179     @LargeTest
testAppAnrIncremental()180     public void testAppAnrIncremental() throws Exception {
181         if (!Utils.hasIncrementalFeature(getDevice())) {
182             return;
183         }
184         final int atomTag = AtomsProto.Atom.ANR_OCCURRED_FIELD_NUMBER;
185         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
186                 atomTag, /*useUidAttributionChain=*/false);
187         final int ANR_WAIT_MILLS = 15_000;
188 
189         try (AutoCloseable a = DeviceUtils.withActivity(getDevice(),
190                 DeviceUtils.STATSD_ATOM_TEST_PKG, "ANRActivity", null, null)) {
191             RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
192             getDevice().executeShellCommand(
193                     "am broadcast -a action_anr -p " + DeviceUtils.STATSD_ATOM_TEST_PKG);
194             RunUtil.getDefault().sleep(ANR_WAIT_MILLS);
195         }
196 
197         // Sorted list of events in order in which they occurred.
198         List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
199 
200         assertThat(data).hasSize(1);
201         assertThat(data.get(0).getAtom().hasAnrOccurred()).isTrue();
202         AtomsProto.ANROccurred atom = data.get(0).getAtom().getAnrOccurred();
203         assertThat(atom.getIsInstantApp().getNumber())
204                 .isEqualTo(AtomsProto.ANROccurred.InstantApp.FALSE_VALUE);
205         assertThat(atom.getForegroundState().getNumber())
206                 .isEqualTo(AtomsProto.ANROccurred.ForegroundState.FOREGROUND_VALUE);
207         assertThat(atom.getErrorSource()).isEqualTo(ErrorSource.DATA_APP);
208         assertThat(atom.getPackageName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG);
209         assertTrue(atom.getIsIncremental());
210         assertFalse((1.0f - atom.getLoadingProgress()) < 0.0000001f);
211     }
212 }
213