1ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei/*
2ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei * Copyright (C) 2017 The Android Open Source Project
3ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei *
4ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei * Licensed under the Apache License, Version 2.0 (the "License");
5ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei * you may not use this file except in compliance with the License.
6ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei * You may obtain a copy of the License at
7ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei *
8ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei *      http://www.apache.org/licenses/LICENSE-2.0
9ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei *
10ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei * Unless required by applicable law or agreed to in writing, software
11ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei * distributed under the License is distributed on an "AS IS" BASIS,
12ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei * See the License for the specific language governing permissions and
14ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei * limitations under the License.
15ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei */
16ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
17ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleipackage com.android.settings.fuelgauge.anomaly.checker;
18ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
19ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport static com.google.common.truth.Truth.assertThat;
20ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
21ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport static org.mockito.ArgumentMatchers.nullable;
22ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport static org.mockito.Matchers.any;
23ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport static org.mockito.Matchers.anyLong;
24ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport static org.mockito.Matchers.eq;
25ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport static org.mockito.Mockito.doReturn;
26ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport static org.mockito.Mockito.spy;
27ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
28ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport android.content.Context;
29ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport android.content.pm.ApplicationInfo;
30ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport android.os.BatteryStats;
31ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport android.text.format.DateUtils;
32ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
33ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport com.android.internal.os.BatterySipper;
34ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport com.android.internal.os.BatteryStatsHelper;
35ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport com.android.settings.TestConfig;
36ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport com.android.settings.fuelgauge.BatteryUtils;
37ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport com.android.settings.fuelgauge.anomaly.Anomaly;
38ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
39bfc2b11b9357c4508756243f540760458611b3b8jackqdyuleiimport com.android.settings.fuelgauge.anomaly.AnomalyUtils;
40ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
41ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport com.android.settings.testutils.SettingsRobolectricTestRunner;
42ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
43ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport org.junit.Before;
44ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport org.junit.Test;
45ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport org.junit.runner.RunWith;
46ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport org.mockito.Mock;
47ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport org.mockito.MockitoAnnotations;
48ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport org.robolectric.RuntimeEnvironment;
49ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport org.robolectric.annotation.Config;
50b96906a082511a5390700818257c8a215637b9e9jackqdyuleiimport org.robolectric.util.ReflectionHelpers;
51ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
52ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport java.util.ArrayList;
53ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleiimport java.util.List;
54ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
55ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei@RunWith(SettingsRobolectricTestRunner.class)
56ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
57ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyuleipublic class BluetoothScanAnomalyDetectorTest {
58ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private static final String TARGET_PACKAGE_NAME = "com.android.app";
59ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private static final int ANOMALY_UID = 111;
60ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private static final int NORMAL_UID = 222;
61ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private static final int TARGET_UID = 333;
62ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private static final long ANOMALY_BLUETOOTH_SCANNING_TIME = DateUtils.HOUR_IN_MILLIS;
63ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private static final long NORMAL_BLUETOOTH_SCANNING_TIME = DateUtils.MINUTE_IN_MILLIS;
64ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    @Mock
65ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private BatteryStatsHelper mBatteryStatsHelper;
66ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    @Mock
67ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private BatterySipper mAnomalySipper;
68ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    @Mock
69ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private BatterySipper mNormalSipper;
70ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    @Mock
71ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private BatterySipper mTargetSipper;
72ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    @Mock
73ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private BatteryStats.Uid mAnomalyUid;
74ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    @Mock
75ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private BatteryStats.Uid mNormalUid;
76ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    @Mock
77ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private BatteryStats.Uid mTargetUid;
78ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    @Mock
79ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private BatteryUtils mBatteryUtils;
80ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    @Mock
81ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private AnomalyDetectionPolicy mPolicy;
82ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    @Mock
83ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private AnomalyAction mAnomalyAction;
84bfc2b11b9357c4508756243f540760458611b3b8jackqdyulei    @Mock
85bfc2b11b9357c4508756243f540760458611b3b8jackqdyulei    private AnomalyUtils mAnomalyUtils;
86ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
87ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private BluetoothScanAnomalyDetector mBluetoothScanAnomalyDetector;
88ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private Context mContext;
89ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private List<BatterySipper> mUsageList;
90ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
91ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    @Before
92ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    public void setUp() {
93ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        MockitoAnnotations.initMocks(this);
94ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
95ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        mContext = spy(RuntimeEnvironment.application);
96b96906a082511a5390700818257c8a215637b9e9jackqdyulei        ReflectionHelpers.setField(mPolicy, "bluetoothScanThreshold",
97b96906a082511a5390700818257c8a215637b9e9jackqdyulei                30 * DateUtils.MINUTE_IN_MILLIS);
98bfc2b11b9357c4508756243f540760458611b3b8jackqdyulei        doReturn(mAnomalyAction).when(mAnomalyUtils).getAnomalyAction(any());
99ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
100ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        mAnomalySipper.uidObj = mAnomalyUid;
101ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        doReturn(ANOMALY_UID).when(mAnomalyUid).getUid();
102ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        mNormalSipper.uidObj = mNormalUid;
103ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        doReturn(NORMAL_UID).when(mNormalUid).getUid();
104ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        mTargetSipper.uidObj = mTargetUid;
105ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        doReturn(TARGET_UID).when(mTargetUid).getUid();
106ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
107ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        mUsageList = new ArrayList<>();
108ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        mUsageList.add(mAnomalySipper);
109ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        mUsageList.add(mNormalSipper);
110ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        mUsageList.add(mTargetSipper);
111ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList();
112ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
113bfc2b11b9357c4508756243f540760458611b3b8jackqdyulei        mBluetoothScanAnomalyDetector = spy(new BluetoothScanAnomalyDetector(mContext, mPolicy,
114bfc2b11b9357c4508756243f540760458611b3b8jackqdyulei                mAnomalyUtils));
115ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        mBluetoothScanAnomalyDetector.mBatteryUtils = mBatteryUtils;
116ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        doReturn(false).when(mBatteryUtils).shouldHideSipper(any());
117ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        doReturn(true).when(mAnomalyAction).isActionActive(any());
118ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
119ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        doReturn(ANOMALY_BLUETOOTH_SCANNING_TIME).when(
120ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei                mBluetoothScanAnomalyDetector).getBluetoothUnoptimizedBgTimeMs(eq(mAnomalyUid),
121ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei                anyLong());
122ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        doReturn(ANOMALY_BLUETOOTH_SCANNING_TIME).when(
123ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei                mBluetoothScanAnomalyDetector).getBluetoothUnoptimizedBgTimeMs(eq(mTargetUid),
124ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei                anyLong());
125ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        doReturn(NORMAL_BLUETOOTH_SCANNING_TIME).when(
126ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei                mBluetoothScanAnomalyDetector).getBluetoothUnoptimizedBgTimeMs(eq(mNormalUid),
127ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei                anyLong());
128ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    }
129ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
130ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    @Test
131ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    public void testDetectAnomalies_containsAnomaly_detectIt() {
132ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        doReturn(-1).when(mBatteryUtils).getPackageUid(nullable(String.class));
133ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        final Anomaly anomaly = createBluetoothAnomaly(ANOMALY_UID);
134ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        final Anomaly targetAnomaly = createBluetoothAnomaly(TARGET_UID);
135ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
136ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        List<Anomaly> mAnomalies = mBluetoothScanAnomalyDetector.detectAnomalies(
137ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei                mBatteryStatsHelper);
138ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
139ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        assertThat(mAnomalies).containsExactly(anomaly, targetAnomaly);
140ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    }
141ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
142ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    @Test
143ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    public void testDetectAnomalies_detectTargetAnomaly_detectIt() {
144ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        doReturn(TARGET_UID).when(mBatteryUtils).getPackageUid(TARGET_PACKAGE_NAME);
145ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        final Anomaly targetAnomaly = createBluetoothAnomaly(TARGET_UID);
146ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
147ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        List<Anomaly> mAnomalies = mBluetoothScanAnomalyDetector.detectAnomalies(
148ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei                mBatteryStatsHelper, TARGET_PACKAGE_NAME);
149ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
150ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        assertThat(mAnomalies).containsExactly(targetAnomaly);
151ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
152ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    }
153ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
154ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    private Anomaly createBluetoothAnomaly(int uid) {
155ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei        return new Anomaly.Builder()
156ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei                .setUid(uid)
157ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei                .setType(Anomaly.AnomalyType.BLUETOOTH_SCAN)
158edfebbaa4f3ffa98c99e1e41b054853ccbff06c7jackqdyulei                .setBluetoothScanningTimeMs(ANOMALY_BLUETOOTH_SCANNING_TIME)
159ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei                .build();
160ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei    }
161ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei
162ec8e127ed3a616a7ca223a5bc6d9c4bbc036cd90jackqdyulei}
163