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