1 /*
2  * Copyright (C) 2019 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.am;
18 
19 import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
20 
21 import static com.android.server.am.ActivityManagerService.Injector;
22 
23 import static com.google.common.truth.Truth.assertThat;
24 
25 import static org.mockito.Mockito.doReturn;
26 import static org.mockito.Mockito.spy;
27 
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.content.pm.ApplicationInfo;
31 import android.content.pm.PackageManagerInternal;
32 import android.os.Handler;
33 import android.os.HandlerThread;
34 import android.os.MessageQueue;
35 import android.os.Process;
36 import android.platform.test.annotations.Presubmit;
37 import android.provider.DeviceConfig;
38 import android.text.TextUtils;
39 
40 import androidx.test.platform.app.InstrumentationRegistry;
41 
42 import com.android.modules.utils.testing.TestableDeviceConfig;
43 import com.android.server.ExtendedMockitoRule;
44 import com.android.server.LocalServices;
45 import com.android.server.ServiceThread;
46 import com.android.server.appop.AppOpsService;
47 import com.android.server.wm.ActivityTaskManagerService;
48 
49 import org.junit.After;
50 import org.junit.Assume;
51 import org.junit.Before;
52 import org.junit.Rule;
53 import org.junit.Test;
54 import org.mockito.Mock;
55 
56 import java.io.File;
57 import java.io.IOException;
58 import java.util.HashSet;
59 import java.util.Set;
60 import java.util.concurrent.CountDownLatch;
61 import java.util.concurrent.TimeUnit;
62 
63 /**
64  * Tests for {@link CachedAppOptimizer}.
65  *
66  * Build/Install/Run:
67  * atest FrameworksMockingServicesTests:CachedAppOptimizerTest
68  */
69 @Presubmit
70 public final class CachedAppOptimizerTest {
71 
72     private ServiceThread mThread;
73 
74     @Mock
75     private AppOpsService mAppOpsService;
76     private CachedAppOptimizer mCachedAppOptimizerUnderTest;
77     private HandlerThread mHandlerThread;
78     private Handler mHandler;
79     private CountDownLatch mCountDown;
80     private ActivityManagerService mAms;
81     private Context mContext;
82     private TestInjector mInjector;
83     private TestProcessDependencies mProcessDependencies;
84 
85     @Mock
86     private PackageManagerInternal mPackageManagerInt;
87 
88     private final TestableDeviceConfig mDeviceConfig = new TestableDeviceConfig();
89 
90     @Rule
91     public final ApplicationExitInfoTest.ServiceThreadRule
92             mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
93 
94     @Rule
95     public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
96             .dynamiclyConfigureSessionBuilder(
97                     sessionBuilder -> mDeviceConfig.setUpMockedClasses(sessionBuilder))
98             .build();
99 
100     @Before
setUp()101     public void setUp() {
102         System.loadLibrary("mockingservicestestjni");
103         mDeviceConfig.setUpMockBehaviors();
104         mHandlerThread = new HandlerThread("");
105         mHandlerThread.start();
106         mHandler = new Handler(mHandlerThread.getLooper());
107         mThread = new ServiceThread("TestServiceThread", Process.THREAD_PRIORITY_DEFAULT,
108                 true /* allowIo */);
109         mThread.start();
110         mContext = InstrumentationRegistry.getInstrumentation().getContext();
111         mInjector = new TestInjector(mContext);
112         mAms = new ActivityManagerService(
113                 new TestInjector(mContext), mServiceThreadRule.getThread());
114         doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
115         mProcessDependencies = new TestProcessDependencies();
116         mCachedAppOptimizerUnderTest = new CachedAppOptimizer(mAms,
117                 new CachedAppOptimizer.PropertyChangedCallbackForTest() {
118                     @Override
119                     public void onPropertyChanged() {
120                         if (mCountDown != null) {
121                             mCountDown.countDown();
122                         }
123                     }
124                 }, mProcessDependencies);
125         LocalServices.removeServiceForTest(PackageManagerInternal.class);
126         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
127     }
128 
129     @After
tearDown()130     public void tearDown() {
131         mHandlerThread.quit();
132         mThread.quit();
133         mCountDown = null;
134         mDeviceConfig.tearDown();
135     }
136 
makeProcessRecord(int pid, int uid, int packageUid, String processName, String packageName)137     private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, String processName,
138             String packageName) {
139         ApplicationInfo ai = new ApplicationInfo();
140         ai.packageName = packageName;
141         ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid);
142         app.setPid(pid);
143         app.info.uid = packageUid;
144         // Exact value does not mater, it can be any state for which compaction is allowed.
145         app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
146         app.mState.setSetAdj(899);
147         app.mState.setCurAdj(940);
148         return app;
149     }
150 
151     @Test
init_setsDefaults()152     public void init_setsDefaults() {
153         mCachedAppOptimizerUnderTest.init();
154         synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
155             assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
156                     CachedAppOptimizer.DEFAULT_USE_COMPACTION);
157             assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
158                     CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
159             assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
160                     CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
161             assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
162                     CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
163             assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
164                     CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
165             assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
166                     CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
167             assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
168                     CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
169             assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
170                     CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
171             assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
172                     CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
173             assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
174                     CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);
175             assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
176                     CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ);
177         }
178 
179 
180         Set<Integer> expected = new HashSet<>();
181         for (String s : TextUtils.split(
182                 CachedAppOptimizer.DEFAULT_COMPACT_PROC_STATE_THROTTLE, ",")) {
183             expected.add(Integer.parseInt(s));
184         }
185         assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
186                 .containsExactlyElementsIn(expected);
187 
188         Assume.assumeTrue(mCachedAppOptimizerUnderTest.isFreezerSupported());
189         assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
190                 CachedAppOptimizer.DEFAULT_USE_FREEZER);
191     }
192 
193     @SuppressWarnings("GuardedBy")
194     @Test
init_withDeviceConfigSetsParameters()195     public void init_withDeviceConfigSetsParameters() {
196         // When the DeviceConfig already has a flag value stored (note this test will need to
197         // change if the default value changes from false).
198         assertThat(CachedAppOptimizer.DEFAULT_USE_COMPACTION).isTrue();
199         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
200                 CachedAppOptimizer.KEY_USE_COMPACTION, "true", false);
201         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
202                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_1,
203                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
204         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
205                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_2,
206                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
207         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
208                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_3,
209                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
210         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
211                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_4,
212                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
213         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
214                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_5,
215                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1), false);
216         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
217                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_6,
218                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1), false);
219         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
220                 CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
221                 Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
222         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
223                 CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE,
224                 Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
225         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
226                 CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB,
227                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false);
228         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
229                 CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB,
230                 Long.toString(
231                         CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false);
232         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
233                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ,
234                 Long.toString(
235                         CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 10), false);
236         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
237                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ,
238                 Long.toString(
239                     CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 10), false);
240         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
241                 CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false);
242         assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
243         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
244                 CachedAppOptimizer.KEY_USE_FREEZER, CachedAppOptimizer.DEFAULT_USE_FREEZER
245                         ? "false" : "true", false);
246 
247         // Then calling init will read and set that flag.
248         mCachedAppOptimizerUnderTest.init();
249         assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isTrue();
250         assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue();
251 
252         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
253                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1);
254         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
255                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1);
256         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
257                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1);
258         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
259                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1);
260         assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
261                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1);
262         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
263                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 10);
264         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
265                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 10);
266         assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
267                 CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
268         assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
269                 CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
270         assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
271                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
272         assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
273 
274         Assume.assumeTrue(CachedAppOptimizer.isFreezerSupported());
275         if (CachedAppOptimizer.isFreezerSupported()) {
276             if (CachedAppOptimizer.DEFAULT_USE_FREEZER) {
277                 assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
278             } else {
279                 assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isTrue();
280             }
281         }
282     }
283 
284     @Test
useCompaction_listensToDeviceConfigChanges()285     public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException {
286         assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
287                 CachedAppOptimizer.DEFAULT_USE_COMPACTION);
288         // When we call init and change some the flag value...
289         mCachedAppOptimizerUnderTest.init();
290         mCountDown = new CountDownLatch(1);
291         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
292                 CachedAppOptimizer.KEY_USE_COMPACTION, "true", false);
293         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
294 
295         // Then that new flag value is updated in the implementation.
296         assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isTrue();
297         assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue();
298 
299         // And again, setting the flag the other way.
300         mCountDown = new CountDownLatch(1);
301         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
302                 CachedAppOptimizer.KEY_USE_COMPACTION, "false", false);
303         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
304         assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isFalse();
305     }
306 
307     @Test
useFreeze_doesNotListenToDeviceConfigChanges()308     public void useFreeze_doesNotListenToDeviceConfigChanges() throws InterruptedException {
309         Assume.assumeTrue(CachedAppOptimizer.isFreezerSupported());
310 
311         assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
312 
313         // The freezer DeviceConfig property is read at boot only
314         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
315                 CachedAppOptimizer.KEY_USE_FREEZER, "true", false);
316         mCachedAppOptimizerUnderTest.init();
317         assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isTrue();
318         mCountDown = new CountDownLatch(1);
319 
320         // No notifications should get to the cached app optimizer.
321         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isFalse();
322 
323         // The flag value has to be set correctly.
324         assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isTrue();
325         // The cached app optimizer thread must be running.
326         assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue();
327 
328         // Set the flag the other way without rebooting. It shall not change.
329         mCountDown = new CountDownLatch(1);
330         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
331                 CachedAppOptimizer.KEY_USE_FREEZER, "false", false);
332         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
333         assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isTrue();
334 
335         // Now, set the flag to false and restart the cached app optimizer
336         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
337                 CachedAppOptimizer.KEY_USE_FREEZER, "false", false);
338         mCachedAppOptimizerUnderTest.init();
339 
340         // The flag value has to be set correctly.
341         assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
342     }
343 
344     @Test
useCompaction_listensToDeviceConfigChangesBadValues()345     public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
346         assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
347                 CachedAppOptimizer.DEFAULT_USE_COMPACTION);
348         mCachedAppOptimizerUnderTest.init();
349 
350         // When we push an invalid flag value...
351         mCountDown = new CountDownLatch(1);
352         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
353                 CachedAppOptimizer.KEY_USE_COMPACTION, "foobar", false);
354         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
355 
356         // Invalid value is mapped to false
357         assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(false);
358     }
359 
360     @Test
useFreeze_listensToDeviceConfigChangesBadValues()361     public void useFreeze_listensToDeviceConfigChangesBadValues() throws InterruptedException {
362         Assume.assumeTrue(CachedAppOptimizer.isFreezerSupported());
363         assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
364 
365         // When we push an invalid flag value...
366         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
367                 CachedAppOptimizer.KEY_USE_FREEZER, "foobar", false);
368 
369         mCachedAppOptimizerUnderTest.init();
370 
371         // DeviceConfig treats invalid value as false
372         assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
373     }
374 
375     @Test
compactThrottle_listensToDeviceConfigChanges()376     public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException {
377         mCachedAppOptimizerUnderTest.init();
378 
379         // When we override new reasonable throttle values after init...
380         mCountDown = new CountDownLatch(8);
381         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
382                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_1,
383                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
384         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
385                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_2,
386                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
387         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
388                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_3,
389                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
390         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
391                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_4,
392                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
393         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
394                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_5,
395                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1), false);
396         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
397                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_6,
398                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1), false);
399         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
400                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ,
401                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 1), false);
402         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
403                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ,
404                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 1), false);
405         assertThat(mCountDown.await(7, TimeUnit.SECONDS)).isTrue();
406 
407         // Then those flags values are reflected in the compactor.
408         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
409                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1);
410         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
411                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1);
412         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
413                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1);
414         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
415                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1);
416         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
417                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 1);
418         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
419                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 1);
420     }
421 
422     @Test
compactThrottle_listensToDeviceConfigChangesBadValues()423     public void compactThrottle_listensToDeviceConfigChangesBadValues()
424             throws InterruptedException {
425         mCachedAppOptimizerUnderTest.init();
426 
427         // When one of the throttles is overridden with a bad value...
428         mCountDown = new CountDownLatch(1);
429         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
430                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_1, "foo", false);
431         // Then all the throttles have the defaults set.
432         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
433         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
434                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
435         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
436                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
437         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
438                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
439         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
440                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
441 
442         // Repeat for each of the throttle keys.
443         mCountDown = new CountDownLatch(1);
444         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
445                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_2, "foo", false);
446         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
447         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
448                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
449         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
450                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
451         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
452                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
453         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
454                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
455 
456         mCountDown = new CountDownLatch(1);
457         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
458                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_3, "foo", false);
459         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
460         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
461                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
462         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
463                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
464         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
465                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
466         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
467                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
468 
469         mCountDown = new CountDownLatch(1);
470         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
471                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_4, "foo", false);
472         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
473         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
474                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
475         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
476                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
477         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
478                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
479         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
480                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
481 
482         mCountDown = new CountDownLatch(1);
483         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
484                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_5, "foo", false);
485         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
486         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
487                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
488         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
489                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
490         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
491                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
492         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
493                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
494 
495         mCountDown = new CountDownLatch(1);
496         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
497                 CachedAppOptimizer.KEY_COMPACT_THROTTLE_6, "foo", false);
498         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
499         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
500                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
501         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
502                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
503         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
504                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
505         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
506                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
507     }
508 
509     @Test
statsdSampleRate_listensToDeviceConfigChanges()510     public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException {
511         mCachedAppOptimizerUnderTest.init();
512 
513         // When we override mCompactStatsdSampleRate with a reasonable value ...
514         mCountDown = new CountDownLatch(1);
515         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
516                 CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
517                 Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
518         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
519 
520         // Then that override is reflected in the compactor.
521         assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
522                 CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
523 
524         // When we override mFreezerStatsdSampleRate with a reasonable value ...
525         mCountDown = new CountDownLatch(1);
526         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
527                 CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE,
528                 Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
529         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
530 
531         // Then that override is reflected in the compactor.
532         assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
533                 CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
534     }
535 
536     @Test
statsdSampleRate_listensToDeviceConfigChangesBadValues()537     public void statsdSampleRate_listensToDeviceConfigChangesBadValues()
538             throws InterruptedException {
539         mCachedAppOptimizerUnderTest.init();
540 
541         // When we override mCompactStatsdSampleRate with an unreasonable value ...
542         mCountDown = new CountDownLatch(1);
543         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
544                 CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false);
545         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
546 
547         // Then that override is reflected in the compactor.
548         assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
549                 CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
550 
551         // When we override mFreezerStatsdSampleRate with an unreasonable value ...
552         mCountDown = new CountDownLatch(1);
553         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
554                 CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE, "foo", false);
555         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
556 
557         // Then that override is reflected in the freezer.
558         assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
559                 CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
560     }
561 
562     @Test
statsdSampleRate_listensToDeviceConfigChangesOutOfRangeValues()563     public void statsdSampleRate_listensToDeviceConfigChangesOutOfRangeValues()
564             throws InterruptedException {
565         mCachedAppOptimizerUnderTest.init();
566 
567         // When we override mCompactStatsdSampleRate with an value outside of [0..1]...
568         mCountDown = new CountDownLatch(1);
569         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
570                 CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
571                 Float.toString(-1.0f), false);
572         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
573 
574         // Then the values is capped in the range.
575         assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(0.0f);
576 
577         mCountDown = new CountDownLatch(1);
578         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
579                 CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
580                 Float.toString(1.01f), false);
581         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
582 
583         // Then the values is capped in the range.
584         assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(1.0f);
585 
586         // When we override mFreezerStatsdSampleRate with an value outside of [0..1]...
587         mCountDown = new CountDownLatch(1);
588         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
589                 CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE,
590                 Float.toString(-1.0f), false);
591         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
592 
593         // Then the values is capped in the range.
594         assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(0.0f);
595 
596         mCountDown = new CountDownLatch(1);
597         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
598                 CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE,
599                 Float.toString(1.01f), false);
600         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
601 
602         // Then the values is capped in the range.
603         assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(1.0f);
604     }
605 
606     @Test
fullCompactionRssThrottleKb_listensToDeviceConfigChanges()607     public void fullCompactionRssThrottleKb_listensToDeviceConfigChanges()
608             throws InterruptedException {
609         mCachedAppOptimizerUnderTest.init();
610 
611         // When we override mStatsdSampleRate with a reasonable value ...
612         mCountDown = new CountDownLatch(1);
613         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
614                 CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB,
615                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false);
616         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
617 
618         // Then that override is reflected in the compactor.
619         assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
620                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
621     }
622 
623     @Test
fullCompactionRssThrottleKb_listensToDeviceConfigChangesBadValues()624     public void fullCompactionRssThrottleKb_listensToDeviceConfigChangesBadValues()
625             throws InterruptedException {
626         mCachedAppOptimizerUnderTest.init();
627 
628         // When we override mStatsdSampleRate with an unreasonable value ...
629         mCountDown = new CountDownLatch(1);
630         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
631                 CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "foo", false);
632         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
633 
634         // Then that override is reflected in the compactor.
635         assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
636                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
637 
638         mCountDown = new CountDownLatch(1);
639         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
640                 CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "-100", false);
641         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
642 
643         // Then that override is reflected in the compactor.
644         assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
645                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
646     }
647 
648     @Test
fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChanges()649     public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChanges()
650             throws InterruptedException {
651         mCachedAppOptimizerUnderTest.init();
652 
653         // When we override mStatsdSampleRate with a reasonable value ...
654         mCountDown = new CountDownLatch(1);
655         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
656                 CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB,
657                 Long.toString(
658                         CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false);
659         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
660 
661         // Then that override is reflected in the compactor.
662         assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
663                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1);
664     }
665 
666     @Test
fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChangesBadValues()667     public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChangesBadValues()
668             throws InterruptedException {
669         mCachedAppOptimizerUnderTest.init();
670 
671         // When we override mStatsdSampleRate with an unreasonable value ...
672         mCountDown = new CountDownLatch(1);
673         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
674                 CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "foo", false);
675         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
676 
677         // Then that override is reflected in the compactor.
678         assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
679                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
680 
681         mCountDown = new CountDownLatch(1);
682         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
683                 CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "-100", false);
684         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
685 
686         // Then that override is reflected in the compactor.
687         assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
688                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
689     }
690 
691     @Test
procStateThrottle_listensToDeviceConfigChanges()692     public void procStateThrottle_listensToDeviceConfigChanges()
693             throws InterruptedException {
694         mCachedAppOptimizerUnderTest.init();
695         mCountDown = new CountDownLatch(1);
696         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
697                 CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false);
698         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
699         assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
700 
701         mCountDown = new CountDownLatch(1);
702         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
703                 CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "", false);
704         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
705         assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).isEmpty();
706     }
707 
708     @Test
procStateThrottle_listensToDeviceConfigChangesBadValues()709     public void procStateThrottle_listensToDeviceConfigChangesBadValues()
710             throws InterruptedException {
711         mCachedAppOptimizerUnderTest.init();
712 
713         Set<Integer> expected = new HashSet<>();
714         for (String s : TextUtils.split(
715                 CachedAppOptimizer.DEFAULT_COMPACT_PROC_STATE_THROTTLE, ",")) {
716             expected.add(Integer.parseInt(s));
717         }
718 
719         // Not numbers
720         mCountDown = new CountDownLatch(1);
721         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
722                 CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "foo", false);
723         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
724         assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
725                 .containsExactlyElementsIn(expected);
726         mCountDown = new CountDownLatch(1);
727         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
728                 CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,foo", false);
729         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
730         assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
731                 .containsExactlyElementsIn(expected);
732 
733         // Empty splits
734         mCountDown = new CountDownLatch(1);
735         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
736                 CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, ",", false);
737         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
738         assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
739                 .containsExactlyElementsIn(expected);
740         mCountDown = new CountDownLatch(1);
741         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
742                 CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, ",,3", false);
743         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
744         assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
745                 .containsExactlyElementsIn(expected);
746         mCountDown = new CountDownLatch(1);
747         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
748                 CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,,3", false);
749         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
750         assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
751                 .containsExactlyElementsIn(expected);
752     }
753 
754     @SuppressWarnings("GuardedBy")
755     @Test
processWithDeltaRSSTooSmall_notFullCompacted()756     public void processWithDeltaRSSTooSmall_notFullCompacted() throws Exception {
757         // Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set RSS
758         // throttle to 12000.
759         mCachedAppOptimizerUnderTest.init();
760         setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true);
761         setFlag(CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "12000", false);
762         initActivityManagerService();
763 
764         // Simulate RSS anon memory larger than throttle.
765         long[] rssBefore1 =
766                 new long[]{/*totalRSS*/ 10000, /*fileRSS*/ 10000, /*anonRSS*/ 12000, /*swap*/
767                         10000};
768         long[] rssAfter1 =
769                 new long[]{/*totalRSS*/ 9000, /*fileRSS*/ 9000, /*anonRSS*/ 11000, /*swap*/9000};
770         // Delta between rssAfter1 and rssBefore2 is below threshold (500).
771         long[] rssBefore2 =
772                 new long[]{/*totalRSS*/ 9500, /*fileRSS*/ 9500, /*anonRSS*/ 11500, /*swap*/9500};
773         long[] rssAfter2 =
774                 new long[]{/*totalRSS*/ 8000, /*fileRSS*/ 8000, /*anonRSS*/ 9000, /*swap*/8000};
775         // Delta between rssAfter1 and rssBefore3 is above threshold (13000).
776         long[] rssBefore3 =
777                 new long[]{/*totalRSS*/ 10000, /*fileRSS*/ 18000, /*anonRSS*/ 13000, /*swap*/ 7000};
778         long[] rssAfter3 =
779                 new long[]{/*totalRSS*/ 10000, /*fileRSS*/ 11000, /*anonRSS*/ 10000, /*swap*/ 6000};
780         long[] valuesAfter = {};
781         // Process that passes properties.
782         int pid = 1;
783         ProcessRecord processRecord = makeProcessRecord(pid, 2, 3, "p1", "app1");
784 
785         // GIVEN we simulate RSS memory before above thresholds and it is the first time 'p1' is
786         // compacted.
787         mProcessDependencies.setRss(rssBefore1);
788         mProcessDependencies.setRssAfterCompaction(rssAfter1); //
789         // WHEN we try to run compaction
790         mCachedAppOptimizerUnderTest.compactApp(processRecord,
791                 CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
792                 false);
793         waitForHandler();
794         // THEN process IS compacted.
795         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
796         valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get(
797                 pid).getRssAfterCompaction();
798         assertThat(valuesAfter).isEqualTo(rssAfter1);
799 
800         // WHEN delta is below threshold (500).
801         mProcessDependencies.setRss(rssBefore2);
802         mProcessDependencies.setRssAfterCompaction(rssAfter2);
803         // This is to avoid throttle of compacting too soon.
804         processRecord.mOptRecord.setLastCompactTime(
805                 processRecord.mOptRecord.getLastCompactTime() - 10_000);
806         // WHEN we try to run compaction.
807         mCachedAppOptimizerUnderTest.compactApp(processRecord,
808                 CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
809                 false);
810         waitForHandler();
811         // THEN process IS NOT compacted - values after compaction for process 1 should remain the
812         // same as from the last compaction.
813         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
814         valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get(
815                 pid).getRssAfterCompaction();
816         assertThat(valuesAfter).isEqualTo(rssAfter1);
817 
818         // WHEN delta is above threshold (13000).
819         mProcessDependencies.setRss(rssBefore3);
820         mProcessDependencies.setRssAfterCompaction(rssAfter3);
821         // This is to avoid throttle of compacting too soon.
822         processRecord.mOptRecord.setLastCompactTime(
823                 processRecord.mOptRecord.getLastCompactTime() - 10_000);
824         // WHEN we try to run compaction
825         mCachedAppOptimizerUnderTest.compactApp(processRecord,
826                 CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
827                 false);
828         waitForHandler();
829         // THEN process IS compacted - values after compaction for process 1 should be updated.
830         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
831         valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get(
832                 pid).getRssAfterCompaction();
833         assertThat(valuesAfter).isEqualTo(rssAfter3);
834     }
835 
836     @SuppressWarnings("GuardedBy")
837     @Test
processWithAnonRSSTooSmall_notFullCompacted()838     public void processWithAnonRSSTooSmall_notFullCompacted() throws Exception {
839         // Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set RSS
840         // throttle to 8000.
841         mCachedAppOptimizerUnderTest.init();
842         setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true);
843         setFlag(CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "8000", false);
844         initActivityManagerService();
845 
846         // Simulate RSS anon memory larger than throttle.
847         long[] rssBelowThreshold =
848                 new long[]{/*Total RSS*/ 10000, /*File RSS*/ 10000, /*Anon RSS*/ 7000, /*Swap*/
849                         10000};
850         long[] rssBelowThresholdAfter =
851                 new long[]{/*Total RSS*/ 9000, /*File RSS*/ 7000, /*Anon RSS*/ 4000, /*Swap*/
852                         8000};
853         long[] rssAboveThreshold =
854                 new long[]{/*Total RSS*/ 10000, /*File RSS*/ 10000, /*Anon RSS*/ 9000, /*Swap*/
855                         10000};
856         long[] rssAboveThresholdAfter =
857                 new long[]{/*Total RSS*/ 8000, /*File RSS*/ 9000, /*Anon RSS*/ 6000, /*Swap*/5000};
858         // Process that passes properties.
859         int pid = 1;
860         ProcessRecord processRecord =
861                 makeProcessRecord(pid, 2, 3, "p1",
862                         "app1");
863 
864         // GIVEN we simulate RSS memory before below threshold.
865         mProcessDependencies.setRss(rssBelowThreshold);
866         mProcessDependencies.setRssAfterCompaction(rssBelowThresholdAfter);
867         // WHEN we try to run compaction
868         mCachedAppOptimizerUnderTest.compactApp(processRecord,
869                 CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
870                 false);
871         waitForHandler();
872         // THEN process IS NOT compacted.
873         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
874 
875         // GIVEN we simulate RSS memory before above threshold.
876         mProcessDependencies.setRss(rssAboveThreshold);
877         mProcessDependencies.setRssAfterCompaction(rssAboveThresholdAfter);
878         // WHEN we try to run compaction
879         mCachedAppOptimizerUnderTest.compactApp(processRecord,
880                 CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
881                 false);
882         waitForHandler();
883         // THEN process IS compacted.
884         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
885         long[] valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get(
886                 pid).getRssAfterCompaction();
887         assertThat(valuesAfter).isEqualTo(rssAboveThresholdAfter);
888     }
889 
890     @SuppressWarnings("GuardedBy")
891     @Test
processWithOomAdjTooSmall_notFullCompacted()892     public void processWithOomAdjTooSmall_notFullCompacted() throws Exception {
893         // Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set Min and
894         // Max OOM_Adj throttles.
895         mCachedAppOptimizerUnderTest.init();
896         setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true);
897         setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, Long.toString(920), true);
898         setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, Long.toString(950), true);
899         initActivityManagerService();
900 
901         // Simulate RSS memory for which compaction should occur.
902         long[] rssBefore =
903                 new long[]{/*Total RSS*/ 15000, /*File RSS*/ 15000, /*Anon RSS*/ 15000,
904                         /*Swap*/ 10000};
905         long[] rssAfter =
906                 new long[]{/*Total RSS*/ 8000, /*File RSS*/ 9000, /*Anon RSS*/ 6000, /*Swap*/
907                         5000};
908         // Process that passes properties.
909         int pid = 1;
910         ProcessRecord processRecord =
911                 makeProcessRecord(pid, 2, 3, "p1", "app1");
912         mProcessDependencies.setRss(rssBefore);
913         mProcessDependencies.setRssAfterCompaction(rssAfter);
914 
915         // When moving within cached state
916         mCachedAppOptimizerUnderTest.onProcessFrozen(processRecord);
917         waitForHandler();
918         // THEN process IS compacted.
919         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
920         long[] valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats
921                 .get(pid)
922                 .getRssAfterCompaction();
923         assertThat(valuesAfter).isEqualTo(rssAfter);
924     }
925 
926     @SuppressWarnings("GuardedBy")
927     @Test
process_forceCompacted()928     public void process_forceCompacted() throws Exception {
929         mCachedAppOptimizerUnderTest.init();
930         setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true);
931         setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, Long.toString(920), true);
932         setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, Long.toString(950), true);
933         initActivityManagerService();
934 
935         long[] rssBefore = new long[] {/*Total RSS*/ 15000, /*File RSS*/ 15000, /*Anon RSS*/ 15000,
936                 /*Swap*/ 10000};
937         long[] rssAfter = new long[] {
938                 /*Total RSS*/ 8000, /*File RSS*/ 9000, /*Anon RSS*/ 6000, /*Swap*/ 5000};
939         // Process that passes properties.
940         int pid = 1;
941         ProcessRecord processRecord = makeProcessRecord(pid, 2, 3, "p1", "app1");
942         mProcessDependencies.setRss(rssBefore);
943         mProcessDependencies.setRssAfterCompaction(rssAfter);
944 
945         // Use an OOM Adjust value that usually avoids compaction
946         processRecord.mState.setSetAdj(100);
947         processRecord.mState.setCurAdj(100);
948 
949         // Compact process full
950         mCachedAppOptimizerUnderTest.compactApp(processRecord,
951                 CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
952                 false);
953         waitForHandler();
954         // the process is not compacted
955         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
956 
957         // Compact process some
958         mCachedAppOptimizerUnderTest.compactApp(processRecord,
959                 CachedAppOptimizer.CompactProfile.SOME, CachedAppOptimizer.CompactSource.APP,
960                 false);
961         waitForHandler();
962         // the process is not compacted
963         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
964 
965         processRecord.mState.setSetAdj(100);
966         processRecord.mState.setCurAdj(100);
967 
968         // We force a full compaction
969         mCachedAppOptimizerUnderTest.compactApp(processRecord,
970                 CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
971                 true);
972         waitForHandler();
973         // then process is compacted.
974         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
975 
976         mCachedAppOptimizerUnderTest.mLastCompactionStats.clear();
977 
978         if (CachedAppOptimizer.ENABLE_FILE_COMPACT) {
979             // We force a some compaction
980             mCachedAppOptimizerUnderTest.compactApp(processRecord,
981                     CachedAppOptimizer.CompactProfile.SOME, CachedAppOptimizer.CompactSource.APP,
982                     true);
983             waitForHandler();
984             // then process is compacted.
985             CachedAppOptimizer.CompactProfile executedCompactProfile =
986                     processRecord.mOptRecord.getLastCompactProfile();
987             assertThat(executedCompactProfile).isEqualTo(CachedAppOptimizer.CompactProfile.SOME);
988         }
989     }
990 
setFlag(String key, String value, boolean defaultValue)991     private void setFlag(String key, String value, boolean defaultValue) throws Exception {
992         mCountDown = new CountDownLatch(1);
993         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, key, value, defaultValue);
994         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
995     }
996 
waitForHandler()997     private void waitForHandler() {
998         Idle idle = new Idle();
999         mCachedAppOptimizerUnderTest.mCompactionHandler.getLooper().getQueue().addIdleHandler(idle);
1000         mCachedAppOptimizerUnderTest.mCompactionHandler.post(() -> { });
1001         idle.waitForIdle();
1002     }
1003 
initActivityManagerService()1004     private void initActivityManagerService() {
1005         mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread());
1006         mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
1007         mAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
1008         mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal());
1009         mAms.mPackageManagerInt = mPackageManagerInt;
1010     }
1011 
1012     private static final class Idle implements MessageQueue.IdleHandler {
1013         private boolean mIdle;
1014 
1015         @Override
queueIdle()1016         public boolean queueIdle() {
1017             synchronized (this) {
1018                 mIdle = true;
1019                 notifyAll();
1020             }
1021             return false;
1022         }
1023 
waitForIdle()1024         public synchronized void waitForIdle() {
1025             while (!mIdle) {
1026                 try {
1027                     // Wait with a timeout of 10s.
1028                     wait(10000);
1029                 } catch (InterruptedException e) {
1030                 }
1031             }
1032         }
1033     }
1034 
1035     private class TestInjector extends Injector {
1036 
TestInjector(Context context)1037         TestInjector(Context context) {
1038             super(context);
1039         }
1040 
1041         @Override
getAppOpsService(File recentAccessesFile, File storageFile, Handler handler)1042         public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
1043                 Handler handler) {
1044             return mAppOpsService;
1045         }
1046 
1047         @Override
getUiHandler(ActivityManagerService service)1048         public Handler getUiHandler(ActivityManagerService service) {
1049             return mHandler;
1050         }
1051     }
1052 
1053     // Test implementation for ProcessDependencies.
1054     private static final class TestProcessDependencies
1055             implements CachedAppOptimizer.ProcessDependencies {
1056         private long[] mRss;
1057         private long[] mRssAfterCompaction;
1058 
1059         @Override
getRss(int pid)1060         public long[] getRss(int pid) {
1061             return mRss;
1062         }
1063 
1064         @Override
performCompaction(CachedAppOptimizer.CompactProfile profile, int pid)1065         public void performCompaction(CachedAppOptimizer.CompactProfile profile, int pid)
1066                 throws IOException {
1067             mRss = mRssAfterCompaction;
1068         }
1069 
setRss(long[] newValues)1070         public void setRss(long[] newValues) {
1071             mRss = newValues;
1072         }
1073 
setRssAfterCompaction(long[] newValues)1074         public void setRssAfterCompaction(long[] newValues) {
1075             mRssAfterCompaction = newValues;
1076         }
1077     }
1078 }
1079