1 /* 2 * Copyright (C) 2023 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.ondevicepersonalization.services; 18 19 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ON_DEVICE_PERSONALIZATION_ERROR; 20 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__ODP; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertFalse; 24 import static org.junit.Assert.assertNotEquals; 25 import static org.junit.Assert.assertThrows; 26 import static org.junit.Assert.assertTrue; 27 import static org.mockito.ArgumentMatchers.any; 28 import static org.mockito.ArgumentMatchers.anyString; 29 import static org.mockito.ArgumentMatchers.eq; 30 import static org.mockito.ArgumentMatchers.isA; 31 import static org.mockito.Mockito.spy; 32 import static org.mockito.Mockito.verify; 33 import static org.mockito.Mockito.when; 34 35 import android.adservices.ondevicepersonalization.Constants; 36 import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationConfigServiceCallback; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.content.pm.PackageManager; 40 import android.os.Build; 41 import android.os.IBinder; 42 43 import androidx.test.core.app.ApplicationProvider; 44 import androidx.test.filters.SdkSuppress; 45 import androidx.test.rule.ServiceTestRule; 46 47 import com.android.modules.utils.testing.ExtendedMockitoRule; 48 import com.android.modules.utils.testing.ExtendedMockitoRule.MockStatic; 49 import com.android.ondevicepersonalization.services.data.user.RawUserData; 50 import com.android.ondevicepersonalization.services.data.user.UserDataCollector; 51 import com.android.ondevicepersonalization.services.data.user.UserPrivacyStatus; 52 import com.android.ondevicepersonalization.services.statsd.errorlogging.ClientErrorLogger; 53 54 import org.junit.After; 55 import org.junit.Before; 56 import org.junit.Rule; 57 import org.junit.Test; 58 import org.junit.runner.RunWith; 59 import org.junit.runners.JUnit4; 60 import org.mockito.Mock; 61 import org.mockito.quality.Strictness; 62 63 import java.util.TimeZone; 64 import java.util.concurrent.CountDownLatch; 65 import java.util.concurrent.TimeUnit; 66 import java.util.concurrent.TimeoutException; 67 68 @RunWith(JUnit4.class) 69 @MockStatic(ClientErrorLogger.class) 70 public class OnDevicePersonalizationConfigServiceTest { 71 @Rule 72 public final ExtendedMockitoRule extendedMockitoRule = 73 new ExtendedMockitoRule.Builder(this).setStrictness(Strictness.LENIENT).build(); 74 75 @Rule 76 public final ServiceTestRule serviceRule = new ServiceTestRule(); 77 private Context mContext = spy(ApplicationProvider.getApplicationContext()); 78 private OnDevicePersonalizationConfigServiceDelegate mBinder; 79 private UserPrivacyStatus mUserPrivacyStatus; 80 private RawUserData mUserData; 81 private UserDataCollector mUserDataCollector; 82 @Mock 83 private ClientErrorLogger mMockClientErrorLogger; 84 85 @Before 86 public void setup() throws Exception { 87 88 PhFlagsTestUtil.setUpDeviceConfigPermissions(); 89 PhFlagsTestUtil.disableGlobalKillSwitch(); 90 PhFlagsTestUtil.disablePersonalizationStatusOverride(); 91 when(mContext.checkCallingPermission(anyString())) 92 .thenReturn(PackageManager.PERMISSION_GRANTED); 93 mBinder = new OnDevicePersonalizationConfigServiceDelegate(mContext); 94 mUserPrivacyStatus = UserPrivacyStatus.getInstanceForTest(); 95 mUserPrivacyStatus.setPersonalizationStatusEnabled(false); 96 mUserData = RawUserData.getInstance(); 97 TimeZone pstTime = TimeZone.getTimeZone("GMT-08:00"); 98 TimeZone.setDefault(pstTime); 99 mUserDataCollector = UserDataCollector.getInstanceForTest(mContext); 100 when(ClientErrorLogger.getInstance()).thenReturn(mMockClientErrorLogger); 101 } 102 103 @Test 104 public void testThrowIfGlobalKillSwitchEnabled() throws Exception { 105 PhFlagsTestUtil.enableGlobalKillSwitch(); 106 try { 107 assertThrows( 108 IllegalStateException.class, 109 () -> 110 mBinder.setPersonalizationStatus(true, null) 111 ); 112 } finally { 113 PhFlagsTestUtil.disableGlobalKillSwitch(); 114 } 115 } 116 117 @Test 118 public void testSetPersonalizationStatusNoCallingPermission() throws Exception { 119 when(mContext.checkCallingPermission(anyString())) 120 .thenReturn(PackageManager.PERMISSION_DENIED); 121 assertThrows(SecurityException.class, () -> { 122 mBinder.setPersonalizationStatus(true, null); 123 }); 124 } 125 126 @Test 127 public void testSetPersonalizationStatusChanged() throws Exception { 128 assertFalse(mUserPrivacyStatus.isPersonalizationStatusEnabled()); 129 populateUserData(); 130 assertNotEquals(0, mUserData.utcOffset); 131 assertTrue(mUserDataCollector.isInitialized()); 132 133 CountDownLatch latch = new CountDownLatch(1); 134 mBinder.setPersonalizationStatus(true, 135 new IOnDevicePersonalizationConfigServiceCallback.Stub() { 136 @Override 137 public void onSuccess() { 138 latch.countDown(); 139 } 140 141 @Override 142 public void onFailure(int errorCode) { 143 latch.countDown(); 144 } 145 }); 146 147 latch.await(); 148 assertTrue(mUserPrivacyStatus.isPersonalizationStatusEnabled()); 149 150 assertEquals(0, mUserData.utcOffset); 151 assertFalse(mUserDataCollector.isInitialized()); 152 } 153 154 @Test 155 public void testSetPersonalizationStatusIfCallbackMissing() throws Exception { 156 assertThrows(NullPointerException.class, () -> { 157 mBinder.setPersonalizationStatus(true, null); 158 }); 159 } 160 161 @Test 162 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 163 public void testSetPersonalizationStatusThrowsRuntimeException() throws Exception { 164 when(mContext.getSystemService(any(Class.class))).thenThrow(RuntimeException.class); 165 CountDownLatch latch = new CountDownLatch(1); 166 TestCallback callback = new TestCallback(latch); 167 168 mBinder.setPersonalizationStatus(true, callback); 169 170 assertTrue(latch.await(10000, TimeUnit.MILLISECONDS)); 171 assertEquals(Constants.STATUS_INTERNAL_ERROR, callback.getErrCode()); 172 verify(mMockClientErrorLogger) 173 .logErrorWithExceptionInfo( 174 isA(RuntimeException.class), 175 eq(AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ON_DEVICE_PERSONALIZATION_ERROR), 176 eq(AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__ODP)); 177 } 178 179 @Test 180 public void testSetPersonalizationStatusNoOps() throws Exception { 181 mUserPrivacyStatus.setPersonalizationStatusEnabled(true); 182 183 populateUserData(); 184 assertNotEquals(0, mUserData.utcOffset); 185 int utcOffset = mUserData.utcOffset; 186 assertTrue(mUserDataCollector.isInitialized()); 187 188 CountDownLatch latch = new CountDownLatch(1); 189 mBinder.setPersonalizationStatus(true, 190 new IOnDevicePersonalizationConfigServiceCallback.Stub() { 191 @Override 192 public void onSuccess() { 193 latch.countDown(); 194 } 195 196 @Override 197 public void onFailure(int errorCode) { 198 latch.countDown(); 199 } 200 }); 201 202 latch.await(); 203 204 assertTrue(mUserPrivacyStatus.isPersonalizationStatusEnabled()); 205 // Adult data should not be roll-back'ed 206 assertEquals(utcOffset, mUserData.utcOffset); 207 assertTrue(mUserDataCollector.isInitialized()); 208 } 209 210 @Test 211 public void testWithBoundService() throws TimeoutException { 212 Intent serviceIntent = new Intent(mContext, 213 OnDevicePersonalizationConfigServiceImpl.class); 214 IBinder binder = serviceRule.bindService(serviceIntent); 215 assertTrue(binder instanceof OnDevicePersonalizationConfigServiceDelegate); 216 } 217 218 @After 219 public void tearDown() throws Exception { 220 mUserDataCollector.clearUserData(mUserData); 221 mUserDataCollector.clearMetadata(); 222 } 223 224 private void populateUserData() { 225 mUserDataCollector.updateUserData(mUserData); 226 } 227 228 class TestCallback extends IOnDevicePersonalizationConfigServiceCallback.Stub { 229 230 int mErrCode; 231 CountDownLatch mLatch; 232 233 TestCallback(CountDownLatch latch) { 234 this.mLatch = latch; 235 } 236 237 @Override 238 public void onSuccess() { 239 } 240 241 @Override 242 public void onFailure(int errorCode) { 243 mErrCode = errorCode; 244 mLatch.countDown(); 245 } 246 247 public int getErrCode() { 248 return mErrCode; 249 } 250 } 251 } 252