1 /*
2  * Copyright (C) 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  */
16 
17 package com.android.server.rollback;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.ArgumentMatchers.anyInt;
22 import static org.mockito.ArgumentMatchers.anyString;
23 import static org.mockito.ArgumentMatchers.eq;
24 import static org.mockito.ArgumentMatchers.same;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.times;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.when;
29 
30 import android.content.Context;
31 import android.content.pm.ApplicationInfo;
32 import android.content.pm.PackageInfo;
33 import android.content.pm.PackageInstaller;
34 import android.content.pm.PackageManager;
35 import android.content.pm.VersionedPackage;
36 import android.os.Bundle;
37 
38 import org.junit.Before;
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 import org.junit.runners.JUnit4;
42 
43 import java.util.List;
44 
45 @RunWith(JUnit4.class)
46 public class WatchdogRollbackLoggerTest {
47 
48     private static final VersionedPackage sTestPackageV1 = new VersionedPackage("test.package", 1);
49     private Context mMockContext = mock(Context.class);
50     private PackageManager mMockPm;
51     private ApplicationInfo mApplicationInfo;
52     private PackageInfo mPackageInfo;
53 
54     private static final String LOGGING_PARENT_KEY = "android.content.pm.LOGGING_PARENT";
55     private static final String LOGGING_PARENT_VALUE = "logging.parent";
56     private static final int PACKAGE_INFO_FLAGS = PackageManager.MATCH_APEX
57             | PackageManager.GET_META_DATA;
58     private static final List<String> sFailingPackages =
59             List.of("package1", "package2", "package3");
60 
61     @Before
setUp()62     public void setUp() {
63         mApplicationInfo = new ApplicationInfo();
64         mMockPm = mock(PackageManager.class);
65         when(mMockContext.getPackageManager()).thenReturn(mMockPm);
66         PackageInstaller mockPi = mock(PackageInstaller.class);
67         when(mMockPm.getPackageInstaller()).thenReturn(mockPi);
68         PackageInstaller.SessionInfo mockSessionInfo = mock(PackageInstaller.SessionInfo.class);
69         when(mockPi.getSessionInfo(anyInt())).thenReturn(mockSessionInfo);
70         mPackageInfo = new PackageInfo();
71     }
72 
73     /**
74      * Ensures that null is returned if the application info has no metadata.
75      */
76     @Test
testLogPackageHasNoMetadata()77     public void testLogPackageHasNoMetadata() throws Exception {
78         when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
79         VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
80                 sTestPackageV1);
81         assertThat(logPackage).isNull();
82         verify(mMockPm, times(1)).getPackageInfo(
83                 sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
84     }
85 
86     /**
87      * Ensures that null is returned if the application info does not contain a logging
88      * parent key.
89      */
90     @Test
testLogPackageParentKeyIsNull()91     public void testLogPackageParentKeyIsNull() throws Exception {
92         when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
93         Bundle bundle = new Bundle();
94         bundle.putString(LOGGING_PARENT_KEY, null);
95         mApplicationInfo.metaData = bundle;
96         mPackageInfo.applicationInfo = mApplicationInfo;
97         VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
98                 sTestPackageV1);
99         assertThat(logPackage).isNull();
100         verify(mMockPm, times(1)).getPackageInfo(
101                 sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
102     }
103 
104     /**
105      * Ensures that the logging parent is returned as the logging package, if it exists.
106      */
107     @Test
testLogPackageHasParentKey()108     public void testLogPackageHasParentKey() throws Exception {
109         Bundle bundle = new Bundle();
110         bundle.putString(LOGGING_PARENT_KEY, LOGGING_PARENT_VALUE);
111         mApplicationInfo.metaData = bundle;
112         mPackageInfo.applicationInfo = mApplicationInfo;
113         mPackageInfo.setLongVersionCode(12345L);
114         when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
115         VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
116                 sTestPackageV1);
117         VersionedPackage expectedLogPackage = new VersionedPackage(LOGGING_PARENT_VALUE, 12345);
118         assertThat(logPackage).isEqualTo(expectedLogPackage);
119         verify(mMockPm, times(1)).getPackageInfo(
120                 sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
121 
122     }
123 
124     /**
125      * Ensures that null is returned if Package Manager does not know about the logging parent.
126      */
127     @Test
testLogPackageNameNotFound()128     public void testLogPackageNameNotFound() throws Exception {
129         Bundle bundle = new Bundle();
130         bundle.putString(LOGGING_PARENT_KEY, LOGGING_PARENT_VALUE);
131         mApplicationInfo.metaData = bundle;
132         mPackageInfo.applicationInfo = mApplicationInfo;
133         when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
134         when(mMockPm.getPackageInfo(same(LOGGING_PARENT_VALUE), anyInt())).thenThrow(
135                 new PackageManager.NameNotFoundException());
136         VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
137                 sTestPackageV1);
138         assertThat(logPackage).isNull();
139         verify(mMockPm, times(1)).getPackageInfo(
140                 sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
141     }
142 
143     /**
144      * Ensures that we make the correct Package Manager calls in the case that the failing packages
145      * are correctly configured with parent packages.
146      */
147     @Test
testApexdLoggingCallsWithParents()148     public void testApexdLoggingCallsWithParents() throws Exception {
149         for (String failingPackage: sFailingPackages) {
150             PackageInfo packageInfo = new PackageInfo();
151             ApplicationInfo applicationInfo = new ApplicationInfo();
152             Bundle bundle = new Bundle();
153             bundle.putString(LOGGING_PARENT_KEY, getParent(failingPackage));
154             applicationInfo.metaData = bundle;
155             packageInfo.applicationInfo = applicationInfo;
156             when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
157         }
158 
159         when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
160         WatchdogRollbackLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
161         for (String failingPackage: sFailingPackages) {
162             verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
163             verify(mMockPm, times(1)).getPackageInfo(getParent(failingPackage), 0);
164         }
165     }
166 
167     /**
168      * Ensures that we don't make any calls to parent packages in the case that packages are not
169      * correctly configured with parent packages.
170      */
171     @Test
testApexdLoggingCallsWithNoParents()172     public void testApexdLoggingCallsWithNoParents() throws Exception {
173         for (String failingPackage: sFailingPackages) {
174             PackageInfo packageInfo = new PackageInfo();
175             packageInfo.applicationInfo = new ApplicationInfo();
176             when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
177         }
178         when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
179 
180         WatchdogRollbackLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
181         verify(mMockPm, times(sFailingPackages.size())).getPackageInfo(anyString(), anyInt());
182         for (String failingPackage: sFailingPackages) {
183             verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
184         }
185     }
186 
getParent(String packageName)187     private String getParent(String packageName) {
188         return packageName + "-parent";
189     }
190 }
191