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.server.tare; 18 19 import static android.app.tare.EconomyManager.arcToCake; 20 import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS; 21 22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; 27 28 import static org.junit.Assert.assertEquals; 29 import static org.junit.Assert.fail; 30 import static org.mockito.ArgumentMatchers.any; 31 import static org.mockito.ArgumentMatchers.anyInt; 32 import static org.mockito.Mockito.eq; 33 import static org.mockito.Mockito.mock; 34 35 import android.app.ActivityManager; 36 import android.app.IActivityManager; 37 import android.app.tare.EconomyManager; 38 import android.content.ContentResolver; 39 import android.content.Context; 40 import android.os.BatteryManager; 41 import android.os.Looper; 42 import android.os.PowerManager; 43 import android.os.RemoteException; 44 import android.provider.DeviceConfig; 45 46 import androidx.annotation.NonNull; 47 import androidx.annotation.Nullable; 48 import androidx.test.runner.AndroidJUnit4; 49 50 import org.junit.After; 51 import org.junit.Before; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 import org.mockito.ArgumentMatchers; 55 import org.mockito.Mock; 56 import org.mockito.MockitoSession; 57 import org.mockito.quality.Strictness; 58 import org.mockito.stubbing.Answer; 59 60 @RunWith(AndroidJUnit4.class) 61 public class JobSchedulerEconomicPolicyTest { 62 private JobSchedulerEconomicPolicy mEconomicPolicy; 63 private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder; 64 private final EconomicPolicy.Injector mInjector = new InjectorForTest(); 65 66 private MockitoSession mMockingSession; 67 @Mock 68 private Context mContext; 69 @Mock 70 private InternalResourceService mIrs; 71 72 private static class InjectorForTest extends EconomicPolicy.Injector { 73 public String settingsConstant; 74 75 @Nullable 76 @Override getSettingsGlobalString(@onNull ContentResolver resolver, @NonNull String name)77 String getSettingsGlobalString(@NonNull ContentResolver resolver, @NonNull String name) { 78 return TARE_JOB_SCHEDULER_CONSTANTS.equals(name) ? settingsConstant : null; 79 } 80 } 81 82 @Before setUp()83 public void setUp() { 84 mMockingSession = mockitoSession() 85 .initMocks(this) 86 .strictness(Strictness.LENIENT) 87 .spyStatic(DeviceConfig.class) 88 .startMocking(); 89 90 when(mIrs.getContext()).thenReturn(mContext); 91 when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); 92 when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class)); 93 // Called by Modifiers. 94 when(mContext.getSystemService(BatteryManager.class)) 95 .thenReturn(mock(BatteryManager.class)); 96 when(mContext.getSystemService(PowerManager.class)) 97 .thenReturn(mock(PowerManager.class)); 98 IActivityManager activityManager = ActivityManager.getService(); 99 spyOn(activityManager); 100 try { 101 doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any()); 102 } catch (RemoteException e) { 103 fail("registerUidObserver threw exception: " + e.getMessage()); 104 } 105 106 mDeviceConfigPropertiesBuilder = 107 new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_TARE); 108 doAnswer( 109 (Answer<DeviceConfig.Properties>) invocationOnMock 110 -> mDeviceConfigPropertiesBuilder.build()) 111 .when(() -> DeviceConfig.getProperties( 112 eq(DeviceConfig.NAMESPACE_TARE), ArgumentMatchers.<String>any())); 113 114 // Initialize real objects. 115 // Capture the listeners. 116 mEconomicPolicy = new JobSchedulerEconomicPolicy(mIrs, mInjector); 117 } 118 119 @After tearDown()120 public void tearDown() { 121 if (mMockingSession != null) { 122 mMockingSession.finishMocking(); 123 } 124 } 125 setDeviceConfigCakes(String key, long valCakes)126 private void setDeviceConfigCakes(String key, long valCakes) { 127 mDeviceConfigPropertiesBuilder.setString(key, valCakes + "c"); 128 mEconomicPolicy.setup(mDeviceConfigPropertiesBuilder.build()); 129 } 130 131 @Test testDefaults()132 public void testDefaults() { 133 assertEquals(EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES, 134 mEconomicPolicy.getInitialSatiatedConsumptionLimit()); 135 assertEquals(EconomyManager.DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES, 136 mEconomicPolicy.getMinSatiatedConsumptionLimit()); 137 assertEquals(EconomyManager.DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES, 138 mEconomicPolicy.getMaxSatiatedConsumptionLimit()); 139 140 final String pkgRestricted = "com.pkg.restricted"; 141 when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); 142 assertEquals(0, mEconomicPolicy.getMinSatiatedBalance(0, pkgRestricted)); 143 assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); 144 145 final String pkgExempted = "com.pkg.exempted"; 146 when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); 147 assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES, 148 mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); 149 assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES, 150 mEconomicPolicy.getMaxSatiatedBalance(0, pkgExempted)); 151 152 final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; 153 when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); 154 assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES, 155 mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); 156 assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES, 157 mEconomicPolicy.getMaxSatiatedBalance(0, pkgHeadlessSystemApp)); 158 159 final String pkgUpdater = "com.pkg.updater"; 160 when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(5); 161 assertEquals(5 * EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES 162 + EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES, 163 mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater)); 164 assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES, 165 mEconomicPolicy.getMaxSatiatedBalance(0, pkgUpdater)); 166 // Make sure it doesn't suggest a min balance greater than max. 167 final int updateCount = (int) (EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES 168 / EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES); 169 when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))) 170 .thenReturn(updateCount); 171 assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES, 172 mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater)); 173 174 assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES, 175 mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); 176 assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES, 177 mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); 178 } 179 180 @Test testConstantsUpdating_ValidValues()181 public void testConstantsUpdating_ValidValues() { 182 setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(5)); 183 setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT, arcToCake(2)); 184 setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT, arcToCake(25)); 185 setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(10)); 186 setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(6)); 187 setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP, 188 arcToCake(5)); 189 setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(4)); 190 setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER, 191 arcToCake(1)); 192 193 assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit()); 194 assertEquals(arcToCake(2), mEconomicPolicy.getMinSatiatedConsumptionLimit()); 195 assertEquals(arcToCake(25), mEconomicPolicy.getMaxSatiatedConsumptionLimit()); 196 final String pkgRestricted = "com.pkg.restricted"; 197 when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); 198 assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); 199 assertEquals(arcToCake(10), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); 200 final String pkgExempted = "com.pkg.exempted"; 201 when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); 202 assertEquals(arcToCake(6), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); 203 final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; 204 when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); 205 assertEquals(arcToCake(5), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); 206 assertEquals(arcToCake(4), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); 207 final String pkgUpdater = "com.pkg.updater"; 208 when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(3); 209 assertEquals(arcToCake(4) + 3 * arcToCake(1), 210 mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater)); 211 } 212 213 @Test testConstantsUpdating_InvalidValues()214 public void testConstantsUpdating_InvalidValues() { 215 // Test negatives. 216 setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(-5)); 217 setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT, arcToCake(-5)); 218 setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT, arcToCake(-5)); 219 setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(-1)); 220 setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(-2)); 221 setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP, 222 arcToCake(-3)); 223 setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(-3)); 224 setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER, 225 arcToCake(-4)); 226 227 assertEquals(arcToCake(1), mEconomicPolicy.getInitialSatiatedConsumptionLimit()); 228 assertEquals(arcToCake(1), mEconomicPolicy.getMinSatiatedConsumptionLimit()); 229 assertEquals(arcToCake(1), mEconomicPolicy.getMaxSatiatedConsumptionLimit()); 230 final String pkgRestricted = "com.pkg.restricted"; 231 when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); 232 assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); 233 assertEquals(arcToCake(1), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); 234 final String pkgExempted = "com.pkg.exempted"; 235 when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); 236 assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); 237 final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; 238 when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); 239 assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); 240 assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); 241 final String pkgUpdater = "com.pkg.updater"; 242 when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(5); 243 assertEquals(arcToCake(0) + 5 * arcToCake(0), 244 mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater)); 245 246 // Test min+max reversed. 247 setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT, arcToCake(5)); 248 setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(4)); 249 setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT, arcToCake(3)); 250 setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(10)); 251 setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(11)); 252 setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP, 253 arcToCake(12)); 254 setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(13)); 255 256 assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit()); 257 assertEquals(arcToCake(5), mEconomicPolicy.getMinSatiatedConsumptionLimit()); 258 assertEquals(arcToCake(5), mEconomicPolicy.getMaxSatiatedConsumptionLimit()); 259 assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); 260 assertEquals(arcToCake(13), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); 261 assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); 262 assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); 263 assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); 264 } 265 } 266