• 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.adservices.service.common;
18 
19 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
20 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
21 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
22 
23 import static com.android.adservices.service.common.AppImportanceFilterTest.ApiCallStatsSubject.apiCallStats;
24 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED;
25 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_CLASS__TARGETING;
26 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__GET_TOPICS;
27 
28 import static com.google.common.truth.Truth.assertWithMessage;
29 
30 import static org.junit.Assert.assertThrows;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.verifyZeroInteractions;
33 import static org.mockito.Mockito.when;
34 
35 import android.adservices.common.AdServicesStatusUtils;
36 import android.app.ActivityManager;
37 import android.content.pm.PackageManager;
38 
39 import androidx.annotation.Nullable;
40 
41 import com.android.adservices.service.common.AppImportanceFilter.WrongCallingApplicationStateException;
42 import com.android.adservices.service.stats.AdServicesLogger;
43 import com.android.adservices.service.stats.ApiCallStats;
44 import com.android.dx.mockito.inline.extended.ExtendedMockito;
45 import com.android.modules.utils.build.SdkLevel;
46 
47 import com.google.common.truth.FailureMetadata;
48 import com.google.common.truth.Subject;
49 
50 import org.junit.After;
51 import org.junit.Before;
52 import org.junit.Test;
53 import org.mockito.ArgumentCaptor;
54 import org.mockito.Captor;
55 import org.mockito.Mock;
56 import org.mockito.MockitoSession;
57 
58 import java.util.Collections;
59 import java.util.List;
60 
61 public class AppImportanceFilterTest {
62     private static final int API_CLASS = AD_SERVICES_API_CALLED__API_CLASS__TARGETING;
63     private static final int API_NAME = AD_SERVICES_API_CALLED__API_NAME__GET_TOPICS;
64     private static final int APP_UID = 321;
65     private static final String APP_PACKAGE_NAME = "test.package.name";
66     private static final String SDK_NAME = "sdk.name";
67     private static final String PROCESS_NAME = "process_name";
68 
69     @Mock private PackageManager mPackageManager;
70     @Captor ArgumentCaptor<ApiCallStats> mApiCallStatsArgumentCaptor;
71     @Mock private ActivityManager mActivityManager;
72     @Mock AdServicesLogger mAdServiceLogger;
73 
74     private AppImportanceFilter mAppImportanceFilter;
75     private MockitoSession mMockitoSession;
76 
77     @Before
setUp()78     public void setUp() {
79         mMockitoSession =
80                 ExtendedMockito.mockitoSession()
81                         .mockStatic(SdkLevel.class)
82                         .initMocks(this)
83                         .startMocking();
84 
85         mAppImportanceFilter =
86                 new AppImportanceFilter(
87                         mActivityManager,
88                         mPackageManager,
89                         mAdServiceLogger,
90                         API_CLASS,
91                         () -> IMPORTANCE_FOREGROUND_SERVICE);
92     }
93 
94     @After
tearDown()95     public void tearDown() {
96         mMockitoSession.finishMocking();
97     }
98 
99     @Test
testCalledWithForegroundAppPackageName_onSMinus_succeedBySkippingCheck()100     public void testCalledWithForegroundAppPackageName_onSMinus_succeedBySkippingCheck() {
101         ExtendedMockito.doReturn(false).when(SdkLevel::isAtLeastT);
102 
103         // No exception is thrown
104         mAppImportanceFilter.assertCallerIsInForeground(APP_PACKAGE_NAME, API_NAME, SDK_NAME);
105 
106         // Should short-circuit without invoking anything
107         verifyZeroInteractions(mActivityManager, mAdServiceLogger, mPackageManager);
108     }
109 
110     @Test
testCalledWithForegroundAppPackageName_succeed()111     public void testCalledWithForegroundAppPackageName_succeed() {
112         ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT);
113         when(mActivityManager.getPackageImportance(APP_PACKAGE_NAME))
114                 .thenReturn(IMPORTANCE_FOREGROUND);
115 
116         // No exception is thrown
117         mAppImportanceFilter.assertCallerIsInForeground(APP_PACKAGE_NAME, API_NAME, SDK_NAME);
118 
119         verifyZeroInteractions(mAdServiceLogger, mPackageManager);
120     }
121 
122     @Test
testCalledWithForegroundServiceImportanceAppPackageName_succeed()123     public void testCalledWithForegroundServiceImportanceAppPackageName_succeed() {
124         ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT);
125         when(mActivityManager.getPackageImportance(APP_PACKAGE_NAME))
126                 .thenReturn(IMPORTANCE_FOREGROUND_SERVICE);
127 
128         // No exception is thrown
129         mAppImportanceFilter.assertCallerIsInForeground(APP_PACKAGE_NAME, API_NAME, SDK_NAME);
130 
131         verifyZeroInteractions(mAdServiceLogger, mPackageManager);
132     }
133 
134     @Test
135     public void
testCalledWithLessThanForegroundImportanceAppPackageName_throwsIllegalStateException()136             testCalledWithLessThanForegroundImportanceAppPackageName_throwsIllegalStateException() {
137         ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT);
138         when(mActivityManager.getPackageImportance(APP_PACKAGE_NAME))
139                 .thenReturn(IMPORTANCE_VISIBLE);
140 
141         assertThrows(
142                 WrongCallingApplicationStateException.class,
143                 () ->
144                         mAppImportanceFilter.assertCallerIsInForeground(
145                                 APP_PACKAGE_NAME, API_NAME, SDK_NAME));
146 
147         verifyZeroInteractions(mPackageManager);
148     }
149 
150     @Test
testCalledWithLessThanForegroundImportanceAppPackageName_logsFailure()151     public void testCalledWithLessThanForegroundImportanceAppPackageName_logsFailure() {
152         ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT);
153         when(mActivityManager.getPackageImportance(APP_PACKAGE_NAME))
154                 .thenReturn(IMPORTANCE_VISIBLE);
155 
156         assertThrows(
157                 WrongCallingApplicationStateException.class,
158                 () ->
159                         mAppImportanceFilter.assertCallerIsInForeground(
160                                 APP_PACKAGE_NAME, API_NAME, SDK_NAME));
161 
162         verify(mAdServiceLogger).logApiCallStats(mApiCallStatsArgumentCaptor.capture());
163         assertWithMessage("")
164                 .about(apiCallStats())
165                 .that(mApiCallStatsArgumentCaptor.getValue())
166                 .hasCode(AD_SERVICES_API_CALLED)
167                 .hasApiName(API_NAME)
168                 .hasApiClass(API_CLASS)
169                 .hasResultCode(AdServicesStatusUtils.STATUS_BACKGROUND_CALLER)
170                 .hasSdkPackageName(SDK_NAME)
171                 .hasAppPackageName(APP_PACKAGE_NAME);
172 
173         verifyZeroInteractions(mPackageManager);
174     }
175 
176     @Test
177     public void
testFailureTryingToRetrievePackageImportancePackageName_throwsIllegalStateException()178             testFailureTryingToRetrievePackageImportancePackageName_throwsIllegalStateException() {
179         ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT);
180         when(mActivityManager.getPackageImportance(APP_PACKAGE_NAME))
181                 .thenThrow(
182                         new IllegalStateException("Simulating failure calling activity manager"));
183 
184         assertThrows(
185                 IllegalStateException.class,
186                 () ->
187                         mAppImportanceFilter.assertCallerIsInForeground(
188                                 APP_PACKAGE_NAME, API_NAME, SDK_NAME));
189     }
190 
191     @Test
testCalledWithForegroundAppUid_onSMinus_succeedBySkippingCheck()192     public void testCalledWithForegroundAppUid_onSMinus_succeedBySkippingCheck() {
193         ExtendedMockito.doReturn(false).when(SdkLevel::isAtLeastT);
194 
195         // No exception is thrown
196         mAppImportanceFilter.assertCallerIsInForeground(APP_UID, API_NAME, SDK_NAME);
197 
198         // Should short-circuit without invoking anything
199         verifyZeroInteractions(mActivityManager, mAdServiceLogger, mPackageManager);
200     }
201 
202     @Test
testCalledWithForegroundAppUid_succeed()203     public void testCalledWithForegroundAppUid_succeed() {
204         ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT);
205         when(mActivityManager.getUidImportance(APP_UID)).thenReturn(IMPORTANCE_FOREGROUND);
206 
207         // No exception is thrown
208         mAppImportanceFilter.assertCallerIsInForeground(APP_UID, API_NAME, SDK_NAME);
209 
210         verifyZeroInteractions(mAdServiceLogger);
211     }
212 
213     @Test
testCalledWithForegroundServiceImportanceAppUid_succeed()214     public void testCalledWithForegroundServiceImportanceAppUid_succeed() {
215         ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT);
216         when(mActivityManager.getUidImportance(APP_UID)).thenReturn(IMPORTANCE_FOREGROUND_SERVICE);
217 
218         // No exception is thrown
219         mAppImportanceFilter.assertCallerIsInForeground(APP_UID, API_NAME, SDK_NAME);
220 
221         verifyZeroInteractions(mAdServiceLogger);
222     }
223 
224     @Test
testCalledWithLessThanForegroundImportanceAppUid_throwsIllegalStateException()225     public void testCalledWithLessThanForegroundImportanceAppUid_throwsIllegalStateException() {
226         ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT);
227         when(mActivityManager.getUidImportance(APP_UID)).thenReturn(IMPORTANCE_VISIBLE);
228 
229         assertThrows(
230                 WrongCallingApplicationStateException.class,
231                 () -> mAppImportanceFilter.assertCallerIsInForeground(APP_UID, API_NAME, SDK_NAME));
232     }
233 
234     @Test
testCalledWithLessThanForegroundImportanceAppUid_logsFailure()235     public void testCalledWithLessThanForegroundImportanceAppUid_logsFailure() {
236         ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT);
237         when(mActivityManager.getUidImportance(APP_UID)).thenReturn(IMPORTANCE_VISIBLE);
238         when(mPackageManager.getPackagesForUid(APP_UID))
239                 .thenReturn(new String[] {APP_PACKAGE_NAME});
240 
241         assertThrows(
242                 IllegalStateException.class,
243                 () -> mAppImportanceFilter.assertCallerIsInForeground(APP_UID, API_NAME, SDK_NAME));
244 
245         verify(mAdServiceLogger).logApiCallStats(mApiCallStatsArgumentCaptor.capture());
246         assertWithMessage("")
247                 .about(apiCallStats())
248                 .that(mApiCallStatsArgumentCaptor.getValue())
249                 .hasCode(AD_SERVICES_API_CALLED)
250                 .hasApiName(API_NAME)
251                 .hasApiClass(API_CLASS)
252                 .hasResultCode(AdServicesStatusUtils.STATUS_BACKGROUND_CALLER)
253                 .hasSdkPackageName(SDK_NAME)
254                 .hasAppPackageName(APP_PACKAGE_NAME);
255     }
256 
257     @Test
testCalledWithLessThanForegroundImportanceAppUidAndNullSdkName_logsFailure()258     public void testCalledWithLessThanForegroundImportanceAppUidAndNullSdkName_logsFailure() {
259         ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT);
260         when(mActivityManager.getUidImportance(APP_UID)).thenReturn(IMPORTANCE_VISIBLE);
261         when(mPackageManager.getPackagesForUid(APP_UID))
262                 .thenReturn(new String[] {APP_PACKAGE_NAME});
263 
264         assertThrows(
265                 WrongCallingApplicationStateException.class,
266                 () -> mAppImportanceFilter.assertCallerIsInForeground(APP_UID, API_NAME, null));
267 
268         verify(mAdServiceLogger).logApiCallStats(mApiCallStatsArgumentCaptor.capture());
269         assertWithMessage("")
270                 .about(apiCallStats())
271                 .that(mApiCallStatsArgumentCaptor.getValue())
272                 .hasCode(AD_SERVICES_API_CALLED)
273                 .hasApiName(API_NAME)
274                 .hasApiClass(API_CLASS)
275                 .hasResultCode(AdServicesStatusUtils.STATUS_BACKGROUND_CALLER)
276                 .hasSdkPackageName("")
277                 .hasAppPackageName(APP_PACKAGE_NAME);
278     }
279 
280     @Test
testFailureTryingToRetrievePackageImportanceFromUid_throwsIllegalStateException()281     public void testFailureTryingToRetrievePackageImportanceFromUid_throwsIllegalStateException() {
282         ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT);
283         when(mActivityManager.getUidImportance(APP_UID))
284                 .thenThrow(
285                         new IllegalStateException("Simulating failure calling activity manager"));
286 
287         assertThrows(
288                 IllegalStateException.class,
289                 () -> mAppImportanceFilter.assertCallerIsInForeground(APP_UID, API_NAME, SDK_NAME));
290     }
291 
292     /** Helper to generate a process info object */
generateProcessInfo( String packageName, int importance, int uid)293     private static ActivityManager.RunningAppProcessInfo generateProcessInfo(
294             String packageName, int importance, int uid) {
295         return generateProcessInfo(Collections.singletonList(packageName), importance, uid);
296     }
297 
generateProcessInfo( List<String> packageNames, int importance, int uid)298     private static ActivityManager.RunningAppProcessInfo generateProcessInfo(
299             List<String> packageNames, int importance, int uid) {
300         ActivityManager.RunningAppProcessInfo process =
301                 new ActivityManager.RunningAppProcessInfo(
302                         PROCESS_NAME, 100, packageNames.toArray(new String[0]));
303         process.importance = importance;
304         process.uid = uid;
305         return process;
306     }
307 
308     public static final class ApiCallStatsSubject extends Subject {
apiCallStats()309         public static Factory<ApiCallStatsSubject, ApiCallStats> apiCallStats() {
310             return ApiCallStatsSubject::new;
311         }
312 
313         @Nullable private final ApiCallStats mActual;
314 
ApiCallStatsSubject(FailureMetadata metadata, @Nullable Object actual)315         ApiCallStatsSubject(FailureMetadata metadata, @Nullable Object actual) {
316             super(metadata, actual);
317             this.mActual = (ApiCallStats) actual;
318         }
319 
hasCode(int code)320         public ApiCallStatsSubject hasCode(int code) {
321             check("getCode()").that(mActual.getCode()).isEqualTo(code);
322             return this;
323         }
324 
hasApiClass(int apiClass)325         public ApiCallStatsSubject hasApiClass(int apiClass) {
326             check("getApiClass()").that(mActual.getApiClass()).isEqualTo(apiClass);
327             return this;
328         }
329 
hasApiName(int apiName)330         public ApiCallStatsSubject hasApiName(int apiName) {
331             check("getApiName()").that(mActual.getApiName()).isEqualTo(apiName);
332             return this;
333         }
334 
hasResultCode(int resultCode)335         public ApiCallStatsSubject hasResultCode(int resultCode) {
336             check("getResultCode()").that(mActual.getResultCode()).isEqualTo(resultCode);
337             return this;
338         }
339 
hasSdkPackageName(String sdkPackageName)340         public ApiCallStatsSubject hasSdkPackageName(String sdkPackageName) {
341             check("getSdkPackageName()")
342                     .that(mActual.getSdkPackageName())
343                     .isEqualTo(sdkPackageName);
344             return this;
345         }
346 
hasAppPackageName(String sdkPackageName)347         public ApiCallStatsSubject hasAppPackageName(String sdkPackageName) {
348             check("getAppPackageName()")
349                     .that(mActual.getAppPackageName())
350                     .isEqualTo(sdkPackageName);
351             return this;
352         }
353     }
354 }
355