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