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.cts.packagemanager.stats.host;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import android.cts.statsdatom.lib.AtomTestUtils;
22 import android.cts.statsdatom.lib.ConfigUtils;
23 import android.cts.statsdatom.lib.DeviceUtils;
24 import android.cts.statsdatom.lib.ReportUtils;
25 
26 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
27 import com.android.os.AtomsProto;
28 import com.android.os.StatsLog;
29 import com.android.tradefed.device.DeviceNotAvailableException;
30 import com.android.tradefed.util.RunUtil;
31 
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.stream.Collectors;
39 
40 public class PackageInstallationSessionReportedStatsTests extends PackageManagerStatsTestsBase {
41     private static final int STEP_FREEZE_INSTALL = 6;
42     private static final String TEST_INSTALL_APK = "CtsStatsdAtomEmptyApp.apk";
43     private static final String TEST_INSTALL_APK_V2 = "CtsStatsdAtomEmptyAppV2.apk";
44     private static final String TEST_INSTALL_PACKAGE =
45             "com.android.cts.packagemanager.stats.emptyapp";
46     private static final String HELPER_PACKAGE = "com.android.cts.packagemanager.stats.device";
47     private static final String HELPER_CLASS =
48             ".PackageInstallationSessionReportedStatsTestsHelper";
49     private static final String GET_USER_TYPES_HELPER_METHOD = "getUserTypeIntegers";
50     private static final String GET_USER_TYPES_HELPER_ARG_USER_IDS = "userIds";
51     private static final String GET_USER_TYPES_HELPER_ARG_USER_TYPES = "userTypes";
52     private static final String TEST_INSTALL_STATIC_SHARED_LIB_V1_APK =
53             "CtsStatsdAtomStaticSharedLibProviderV1.apk";
54     private static final String TEST_INSTALL_STATIC_SHARED_LIB_V2_APK =
55             "CtsStatsdAtomStaticSharedLibProviderV2.apk";
56     private static final String TEST_INSTALL_STATIC_SHARED_LIB_V1_PACKAGE =
57             "com.android.cts.packagemanager.stats.emptystaticsharedlib";
58     private static final String TEST_INSTALL_STATIC_SHARED_LIB_NAME = "test.stats.lib";
59 
60     @Override
tearDown()61     protected void tearDown() throws Exception {
62         getDevice().uninstallPackage(TEST_INSTALL_PACKAGE);
63         getDevice().uninstallPackage(TEST_INSTALL_STATIC_SHARED_LIB_V1_PACKAGE);
64         super.tearDown();
65     }
66 
testPackageInstallationSessionReportedForApkSuccessWithReplace()67     public void testPackageInstallationSessionReportedForApkSuccessWithReplace() throws Exception {
68         if (!Utils.hasIncrementalFeature(getDevice())) {
69             return;
70         }
71         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
72                 AtomsProto.Atom.PACKAGE_INSTALLATION_SESSION_REPORTED_FIELD_NUMBER);
73         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
74         DeviceUtils.installTestApp(getDevice(), TEST_INSTALL_APK, TEST_INSTALL_PACKAGE, mCtsBuild);
75         assertThat(getDevice().isPackageInstalled(TEST_INSTALL_PACKAGE,
76                 String.valueOf(getDevice().getCurrentUser()))).isTrue();
77         installPackageUsingIncremental(new String[]{TEST_INSTALL_APK_V2});
78         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
79         List<AtomsProto.PackageInstallationSessionReported> reports = new ArrayList<>();
80         for (StatsLog.EventMetricData data : ReportUtils.getEventMetricDataList(getDevice())) {
81             if (data.getAtom().hasPackageInstallationSessionReported()) {
82                 reports.add(data.getAtom().getPackageInstallationSessionReported());
83             }
84         }
85         assertThat(reports.size()).isEqualTo(2);
86         final int expectedUid = getAppUid(TEST_INSTALL_PACKAGE);
87         final int expectedUser = getDevice().getCurrentUser();
88         final long expectedApksSizeBytes = getTestFileSize(TEST_INSTALL_APK);
89         // TODO(b/249294752): check installer in the report
90         checkReportResult(reports.get(0), expectedUid, Collections.singletonList(expectedUser),
91                 Collections.emptyList(), 1 /* success */, 0 /* internalErrorCode */,
92                 1 /* versionCode */, expectedApksSizeBytes, 0 /* dataLoaderType */,
93                 0 /* expectedUserActionRequiredType */, false,
94                 false, false, false, false, false, false);
95         checkDurationResult(reports.get(0));
96 
97         checkReportResult(reports.get(1), expectedUid, Collections.singletonList(expectedUser),
98                 Collections.singletonList(expectedUser), 1 /* success */, 0 /* internalErrorCode */,
99                 2 /* versionCode */, getTestFileSize(TEST_INSTALL_APK_V2), 2 /* dataLoaderType */,
100                 0 /* expectedUserActionRequiredType */, false,
101                 true, false, false, false, false, false);
102         checkDurationResult(reports.get(1));
103 
104         // No uninstall log from app update
105         List<AtomsProto.PackageUninstallationReported> uninstallReports = new ArrayList<>();
106         for (StatsLog.EventMetricData data : ReportUtils.getEventMetricDataList(getDevice())) {
107             if (data.getAtom().hasPackageUninstallationReported()) {
108                 uninstallReports.add(data.getAtom().getPackageUninstallationReported());
109             }
110         }
111         assertThat(uninstallReports).isEmpty();
112     }
113 
checkDurationResult(AtomsProto.PackageInstallationSessionReported report)114     private void checkDurationResult(AtomsProto.PackageInstallationSessionReported report) {
115         final long totalDuration = report.getTotalDurationMillis();
116         assertThat(totalDuration).isGreaterThan(0);
117         // TODO(b/308138823): Use hortenInstallFreeze(), meet java.lang.NoClassDefFoundError:
118         // android/provider/DeviceConfig error
119         // @RequiresFlagsEnabled still runs even the flag is false.
120         //int expectedSteps = shortenInstallFreeze() ? 5 : 4;
121         assertThat(report.getInstallStepsCount()).isAtLeast(4);
122         long sumStepDurations = 0;
123         int stepCount = report.getInstallStepsCount();
124         for (int i = 0; i < stepCount; i++) {
125             int step = report.getInstallSteps(i);
126             // Don't count freeze step time
127             if (step != STEP_FREEZE_INSTALL) {
128                 long duration = report.getStepDurationMillis(i);
129                 assertThat(duration).isAtLeast(0);
130                 sumStepDurations += duration;
131             }
132         }
133         assertThat(sumStepDurations).isGreaterThan(0);
134         assertThat(sumStepDurations).isLessThan(totalDuration);
135     }
136 
checkReportResult(AtomsProto.PackageInstallationSessionReported report, int expectedUid, List<Integer> expectedUserIds, List<Integer> expectedOriginalUserIds, int expectedPublicReturnCode, int expectedInternalErrorCode, long expectedVersionCode, long expectedApksSizeBytes, int expectedDataLoaderType, int expectedUserActionRequiredType, boolean expectedIsInstant, boolean expectedIsReplace, boolean expectedIsSystem, boolean expectedIsInherit, boolean expectedInstallingExistingAsUser, boolean expectedIsMoveInstall, boolean expectedIsStaged)137     private void checkReportResult(AtomsProto.PackageInstallationSessionReported report,
138             int expectedUid, List<Integer> expectedUserIds,
139             List<Integer> expectedOriginalUserIds, int expectedPublicReturnCode,
140             int expectedInternalErrorCode,
141             long expectedVersionCode, long expectedApksSizeBytes, int expectedDataLoaderType,
142             int expectedUserActionRequiredType,
143             boolean expectedIsInstant, boolean expectedIsReplace, boolean expectedIsSystem,
144             boolean expectedIsInherit, boolean expectedInstallingExistingAsUser,
145             boolean expectedIsMoveInstall, boolean expectedIsStaged)
146             throws DeviceNotAvailableException {
147         assertThat(report.getSessionId()).isNotEqualTo(-1);
148         assertThat(report.getUid()).isEqualTo(expectedUid);
149         assertThat(report.getUserIdsList()).containsAtLeastElementsIn(expectedUserIds);
150         checkUserTypes(expectedUserIds, report.getUserTypesList());
151         assertThat(report.getOriginalUserIdsList()).containsAtLeastElementsIn(
152                 expectedOriginalUserIds);
153         assertThat(report.getPublicReturnCode()).isEqualTo(expectedPublicReturnCode);
154         assertThat(report.getInternalErrorCode()).isEqualTo(expectedInternalErrorCode);
155         assertThat(report.getVersionCode()).isEqualTo(expectedVersionCode);
156         assertThat(report.getApksSizeBytes()).isEqualTo(expectedApksSizeBytes);
157         assertThat(report.getOriginalInstallerPackageUid()).isEqualTo(-1);
158         assertThat(report.getDataLoaderType()).isEqualTo(expectedDataLoaderType);
159         assertThat(report.getUserActionRequiredType()).isEqualTo(expectedUserActionRequiredType);
160         assertThat(report.getIsInstant()).isEqualTo(expectedIsInstant);
161         assertThat(report.getIsReplace()).isEqualTo(expectedIsReplace);
162         assertThat(report.getIsSystem()).isEqualTo(expectedIsSystem);
163         assertThat(report.getIsInherit()).isEqualTo(expectedIsInherit);
164         assertThat(report.getIsInstallingExistingAsUser()).isEqualTo(
165                 expectedInstallingExistingAsUser);
166         assertThat(report.getIsMoveInstall()).isEqualTo(expectedIsMoveInstall);
167         assertThat(report.getIsStaged()).isEqualTo(expectedIsStaged);
168         // assert that package name is not set if install comes from adb
169         if ((report.getInstallFlags() & 0x00000020 /* INSTALL_FROM_ADB */) != 0) {
170             assertThat(report.getPackageName()).isEmpty();
171         }
172     }
173 
checkUserTypes(List<Integer> userIds, List<Integer> reportedUserTypes)174     private void checkUserTypes(List<Integer> userIds, List<Integer> reportedUserTypes)
175             throws DeviceNotAvailableException {
176         if (userIds == null || userIds.isEmpty()) {
177             return;
178         }
179         final HashMap<String, String> testArgs = new HashMap<>();
180         testArgs.put(GET_USER_TYPES_HELPER_ARG_USER_IDS,
181                 userIds.stream().map(Object::toString).collect(Collectors.joining(","))
182         );
183         Map<String, String> testResult = Utils.runDeviceTests(getDevice(), HELPER_PACKAGE,
184                 HELPER_CLASS, GET_USER_TYPES_HELPER_METHOD, testArgs);
185         assertNotNull(testResult);
186         assertEquals(1, testResult.size());
187         String[] userTypesStrings = testResult.get(GET_USER_TYPES_HELPER_ARG_USER_TYPES).split(",");
188         List<Integer> expectedUserTypes = Arrays.stream(userTypesStrings).map(
189                 Integer::valueOf).collect(Collectors.toList());
190         assertThat(reportedUserTypes).containsAtLeastElementsIn(expectedUserTypes);
191     }
192 
testPackageUninstalledReported()193     public void testPackageUninstalledReported() throws Exception {
194         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
195                 AtomsProto.Atom.PACKAGE_UNINSTALLATION_REPORTED_FIELD_NUMBER);
196         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
197         DeviceUtils.installTestApp(getDevice(), TEST_INSTALL_APK, TEST_INSTALL_PACKAGE, mCtsBuild);
198         assertThat(getDevice().isPackageInstalled(TEST_INSTALL_PACKAGE,
199                 String.valueOf(getDevice().getCurrentUser()))).isTrue();
200         final int expectedUid = getAppUid(TEST_INSTALL_PACKAGE);
201         DeviceUtils.uninstallTestApp(getDevice(), TEST_INSTALL_PACKAGE);
202         assertThat(getDevice().isPackageInstalled(TEST_INSTALL_PACKAGE,
203                 String.valueOf(getDevice().getCurrentUser()))).isFalse();
204         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
205         List<AtomsProto.PackageUninstallationReported> reports = new ArrayList<>();
206         for (StatsLog.EventMetricData data : ReportUtils.getEventMetricDataList(getDevice())) {
207             if (data.getAtom().hasPackageUninstallationReported()) {
208                 reports.add(data.getAtom().getPackageUninstallationReported());
209             }
210         }
211         assertThat(reports.size()).isEqualTo(1);
212         AtomsProto.PackageUninstallationReported report = reports.get(0);
213         assertThat(report.getUid()).isEqualTo(expectedUid);
214         final List<Integer> users = Collections.singletonList(getDevice().getCurrentUser());
215         assertThat(report.getUserIdsList()).containsAtLeastElementsIn(users);
216         assertThat(report.getOriginalUserIdsList()).containsAtLeastElementsIn(users);
217         assertThat(report.getUninstallFlags()).isEqualTo(2 /* DELETE_ALL_USERS */);
218         assertThat(report.getReturnCode()).isEqualTo(1);
219         assertThat(report.getIsSystem()).isFalse();
220         assertThat(report.getIsUninstallForUsers()).isFalse();
221     }
222 
testPackageInstallationFailedVersionDowngradeReported()223     public void testPackageInstallationFailedVersionDowngradeReported() throws Exception {
224         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
225                 AtomsProto.Atom.PACKAGE_INSTALLATION_SESSION_REPORTED_FIELD_NUMBER);
226         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
227         DeviceUtils.installTestApp(getDevice(), TEST_INSTALL_APK_V2, TEST_INSTALL_PACKAGE,
228                 mCtsBuild);
229         assertThat(getDevice().isPackageInstalled(TEST_INSTALL_PACKAGE,
230                 String.valueOf(getDevice().getCurrentUser()))).isTrue();
231         // Second install should fail because of version downgrade
232         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
233         final String result = getDevice().installPackage(buildHelper.getTestFile(TEST_INSTALL_APK),
234                 /*reinstall=*/true, /*grantPermissions=*/true);
235         assertThat(result).isNotNull();
236 
237         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
238         List<AtomsProto.PackageInstallationSessionReported> reports = new ArrayList<>();
239         for (StatsLog.EventMetricData data : ReportUtils.getEventMetricDataList(getDevice())) {
240             if (data.getAtom().hasPackageInstallationSessionReported()) {
241                 reports.add(data.getAtom().getPackageInstallationSessionReported());
242             }
243         }
244         assertThat(reports.size()).isEqualTo(2);
245         final int expectedUid = getAppUid(TEST_INSTALL_PACKAGE);
246         final int expectedUser = getDevice().getCurrentUser();
247         checkReportResult(reports.get(0), expectedUid, Collections.singletonList(expectedUser),
248                 Collections.emptyList(), 1 /* success */, 0 /* internalErrorCode */,
249                 2 /* versionCode */, getTestFileSize(TEST_INSTALL_APK_V2), 0 /* dataLoaderType */,
250                 0 /* expectedUserActionRequiredType */, false,
251                 false, false, false, false, false, false);
252         checkDurationResult(reports.get(0));
253         checkReportResult(
254                 reports.get(1), -1 /* uid */, Collections.emptyList(), Collections.emptyList(),
255                 -25 /* INSTALL_FAILED_VERSION_DOWNGRADE */, 0 /* internalErrorCode */,
256                 0 /* versionCode */, 0, 0 /* dataLoaderType */,
257                 0 /* expectedUserActionRequiredType */, false,
258                 false, false, false, false, false, false);
259     }
260 
testPackageInstallationFailedInternalErrorReported()261     public void testPackageInstallationFailedInternalErrorReported() throws Exception {
262         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
263                 AtomsProto.Atom.PACKAGE_INSTALLATION_SESSION_REPORTED_FIELD_NUMBER);
264         Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
265         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
266         String result = getDevice().installPackage(buildHelper.getTestFile(
267                 TEST_INSTALL_STATIC_SHARED_LIB_V1_APK),
268                 /*reinstall=*/true, /*grantPermissions=*/true);
269         assertThat(result).isNull();
270         assertThat(isLibraryInstalled(TEST_INSTALL_STATIC_SHARED_LIB_NAME)).isTrue();
271         // Second install should fail because of static shared lib version order mismatch
272         result = getDevice().installPackage(buildHelper.getTestFile(
273                 TEST_INSTALL_STATIC_SHARED_LIB_V2_APK),
274                 /*reinstall=*/true, /*grantPermissions=*/true);
275         assertThat(result).isNotNull();
276 
277         Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
278         List<AtomsProto.PackageInstallationSessionReported> reports = new ArrayList<>();
279         for (StatsLog.EventMetricData data : ReportUtils.getEventMetricDataList(getDevice())) {
280             if (data.getAtom().hasPackageInstallationSessionReported()) {
281                 reports.add(data.getAtom().getPackageInstallationSessionReported());
282             }
283         }
284         assertThat(reports.size()).isEqualTo(2);
285         final int expectedUid = getAppUid(TEST_INSTALL_STATIC_SHARED_LIB_V1_PACKAGE);
286         final int expectedUser = getDevice().getCurrentUser();
287         checkReportResult(reports.get(0), expectedUid, Collections.singletonList(expectedUser),
288                 Collections.emptyList(), 1 /* success */, 0 /* internalErrorCode */,
289                 1 /* versionCode */, getTestFileSize(TEST_INSTALL_APK_V2), 0 /* dataLoaderType */,
290                 0 /* expectedUserActionRequiredType */, false,
291                 false, false, false, false, false, false);
292         checkDurationResult(reports.get(0));
293         checkReportResult(
294                 reports.get(1), -1 /* uid */, Collections.emptyList(), Collections.emptyList(),
295                 -110 /* INSTALL_FAILED_INTERNAL_ERROR */,
296                 -14 /* INTERNAL_ERROR_STATIC_SHARED_LIB_VERSION_CODES_ORDER */,
297                 0 /* versionCode */,
298                 0, 0 /* dataLoaderType */,
299                 0 /* expectedUserActionRequiredType */, false,
300                 false, false, false, false, false, false);
301     }
302 }
303