1 /*
2  * Copyright (C) 2017 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.settings.testutils;
18 
19 import static org.mockito.Mockito.when;
20 
21 import android.app.settings.SettingsEnums;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.hardware.usb.UsbManager;
25 import android.hardware.usb.UsbPort;
26 import android.hardware.usb.UsbPortStatus;
27 import android.os.BatteryManager;
28 import android.os.UserManager;
29 import android.provider.Settings;
30 
31 import androidx.room.Room;
32 
33 import com.android.settings.DisplaySettings;
34 import com.android.settings.display.ScreenTimeoutSettings;
35 import com.android.settings.fuelgauge.batteryusage.BatteryInformation;
36 import com.android.settings.fuelgauge.batteryusage.ConvertUtils;
37 import com.android.settings.fuelgauge.batteryusage.DeviceBatteryState;
38 import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEvent;
39 import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList;
40 import com.android.settings.fuelgauge.batteryusage.PowerAnomalyKey;
41 import com.android.settings.fuelgauge.batteryusage.PowerAnomalyType;
42 import com.android.settings.fuelgauge.batteryusage.WarningBannerInfo;
43 import com.android.settings.fuelgauge.batteryusage.WarningItemInfo;
44 import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventDao;
45 import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
46 import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
47 import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao;
48 import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
49 
50 import com.google.common.collect.ImmutableList;
51 
52 import org.robolectric.Shadows;
53 
54 import java.util.ArrayList;
55 import java.util.List;
56 
57 public class BatteryTestUtils {
58 
getChargingIntent()59     public static Intent getChargingIntent() {
60         return getCustomBatteryIntent(
61                 BatteryManager.BATTERY_PLUGGED_AC,
62                 50 /* level */,
63                 100 /* scale */,
64                 BatteryManager.BATTERY_STATUS_CHARGING);
65     }
66 
getDischargingIntent()67     public static Intent getDischargingIntent() {
68         return getCustomBatteryIntent(
69                 0 /* plugged */,
70                 10 /* level */,
71                 100 /* scale */,
72                 BatteryManager.BATTERY_STATUS_DISCHARGING);
73     }
74 
75     /** Sets the work profile mode. */
setWorkProfile(Context context)76     public static void setWorkProfile(Context context) {
77         final UserManager userManager = context.getSystemService(UserManager.class);
78         Shadows.shadowOf(userManager).setManagedProfile(true);
79         Shadows.shadowOf(userManager).setIsSystemUser(false);
80     }
81 
82     /** Creates and sets up the in-memory {@link BatteryStateDatabase}. */
setUpBatteryStateDatabase(Context context)83     public static BatteryStateDatabase setUpBatteryStateDatabase(Context context) {
84         final BatteryStateDatabase inMemoryDatabase =
85                 Room.inMemoryDatabaseBuilder(context, BatteryStateDatabase.class)
86                         .allowMainThreadQueries()
87                         .build();
88         BatteryStateDatabase.setBatteryStateDatabase(inMemoryDatabase);
89         return inMemoryDatabase;
90     }
91 
92     /** Inserts a fake data into the database for testing. */
insertDataToBatteryStateTable( Context context, long timestamp, String packageName)93     public static void insertDataToBatteryStateTable(
94             Context context, long timestamp, String packageName) {
95         insertDataToBatteryStateTable(
96                 context,
97                 timestamp,
98                 packageName,
99                 /* multiple= */ false,
100                 /* isFullChargeStart= */ false);
101     }
102 
103     /** Inserts a fake data into the database for testing. */
insertDataToBatteryStateTable( Context context, long timestamp, String packageName, boolean isFullChargeStart)104     public static void insertDataToBatteryStateTable(
105             Context context, long timestamp, String packageName, boolean isFullChargeStart) {
106         insertDataToBatteryStateTable(
107                 context, timestamp, packageName, /* multiple= */ false, isFullChargeStart);
108     }
109 
110     /** Inserts a fake data into the database for testing. */
insertDataToBatteryStateTable( Context context, long timestamp, String packageName, boolean multiple, boolean isFullChargeStart)111     public static void insertDataToBatteryStateTable(
112             Context context,
113             long timestamp,
114             String packageName,
115             boolean multiple,
116             boolean isFullChargeStart) {
117         DeviceBatteryState deviceBatteryState =
118                 DeviceBatteryState.newBuilder()
119                         .setBatteryLevel(31)
120                         .setBatteryStatus(0)
121                         .setBatteryHealth(0)
122                         .build();
123         BatteryInformation batteryInformation =
124                 BatteryInformation.newBuilder()
125                         .setDeviceBatteryState(deviceBatteryState)
126                         .setIsHidden(true)
127                         .setBootTimestamp(timestamp - 1)
128                         .setZoneId("Europe/Paris")
129                         .setAppLabel("Settings")
130                         .setTotalPower(100f)
131                         .setConsumePower(0.3f)
132                         .setPercentOfTotal(10f)
133                         .setDrainType(1)
134                         .setForegroundUsageTimeInMs(60000)
135                         .setBackgroundUsageTimeInMs(10000)
136                         .setForegroundUsageConsumePower(0.1f)
137                         .setForegroundServiceUsageConsumePower(0.05f)
138                         .setBackgroundUsageConsumePower(0.1f)
139                         .setCachedUsageConsumePower(0.05f)
140                         .build();
141 
142         final BatteryState state =
143                 new BatteryState(
144                         /* uid= */ 1001L,
145                         /* userId= */ 100L,
146                         packageName,
147                         timestamp,
148                         /* consumerType= */ 2,
149                         isFullChargeStart,
150                         ConvertUtils.convertBatteryInformationToString(batteryInformation),
151                         "");
152         BatteryStateDao dao = BatteryStateDatabase.getInstance(context).batteryStateDao();
153         if (multiple) {
154             dao.insertAll(ImmutableList.of(state));
155         } else {
156             dao.insert(state);
157         }
158     }
159 
160     /** Inserts a fake data into the database for testing. */
insertDataToAppUsageEventTable( Context context, long userId, long timestamp, String packageName)161     public static void insertDataToAppUsageEventTable(
162             Context context, long userId, long timestamp, String packageName) {
163         insertDataToAppUsageEventTable(
164                 context, userId, timestamp, packageName, /* multiple= */ false);
165     }
166 
167     /** Inserts a fake data into the database for testing. */
insertDataToAppUsageEventTable( Context context, long userId, long timestamp, String packageName, boolean multiple)168     public static void insertDataToAppUsageEventTable(
169             Context context, long userId, long timestamp, String packageName, boolean multiple) {
170         final AppUsageEventEntity entity =
171                 new AppUsageEventEntity(
172                         /* uid= */ 101L,
173                         userId,
174                         timestamp,
175                         /* appUsageEventType= */ 2,
176                         packageName,
177                         /* instanceId= */ 10001,
178                         /* taskRootPackageName= */ "com.android.settings");
179         AppUsageEventDao dao = BatteryStateDatabase.getInstance(context).appUsageEventDao();
180         if (multiple) {
181             dao.insertAll(ImmutableList.of(entity));
182         } else {
183             dao.insert(entity);
184         }
185     }
186 
187     /** Gets customized battery changed intent. */
getCustomBatteryIntent(int plugged, int level, int scale, int status)188     public static Intent getCustomBatteryIntent(int plugged, int level, int scale, int status) {
189         Intent intent = new Intent();
190         intent.putExtra(BatteryManager.EXTRA_PLUGGED, plugged);
191         intent.putExtra(BatteryManager.EXTRA_LEVEL, level);
192         intent.putExtra(BatteryManager.EXTRA_SCALE, scale);
193         intent.putExtra(BatteryManager.EXTRA_STATUS, status);
194 
195         return intent;
196     }
197 
198     /** Configures the incompatible charger environment. */
setupIncompatibleEvent( UsbPort mockUsbPort, UsbManager mockUsbManager, UsbPortStatus mockUsbPortStatus)199     public static void setupIncompatibleEvent(
200             UsbPort mockUsbPort, UsbManager mockUsbManager, UsbPortStatus mockUsbPortStatus) {
201         final List<UsbPort> usbPorts = new ArrayList<>();
202         usbPorts.add(mockUsbPort);
203         when(mockUsbManager.getPorts()).thenReturn(usbPorts);
204         when(mockUsbPort.getStatus()).thenReturn(mockUsbPortStatus);
205         when(mockUsbPort.supportsComplianceWarnings()).thenReturn(true);
206         when(mockUsbPortStatus.isConnected()).thenReturn(true);
207         when(mockUsbPortStatus.getComplianceWarnings())
208                 .thenReturn(new int[] {UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY});
209     }
210 
211     /** Create an empty power anomaly event list proto. */
createEmptyPowerAnomalyEventList()212     public static PowerAnomalyEventList createEmptyPowerAnomalyEventList() {
213         return PowerAnomalyEventList.getDefaultInstance();
214     }
215 
216     /** Create an non-empty power anomaly event list proto. */
createNonEmptyPowerAnomalyEventList()217     public static PowerAnomalyEventList createNonEmptyPowerAnomalyEventList() {
218         return PowerAnomalyEventList.newBuilder()
219                 .addPowerAnomalyEvents(0, createAdaptiveBrightnessAnomalyEvent())
220                 .addPowerAnomalyEvents(1, createScreenTimeoutAnomalyEvent())
221                 .build();
222     }
223 
224     /** Create a power anomaly event proto of adaptive brightness. */
createAdaptiveBrightnessAnomalyEvent()225     public static PowerAnomalyEvent createAdaptiveBrightnessAnomalyEvent() {
226         return createAdaptiveBrightnessAnomalyEvent(false);
227     }
228 
229     /** Create a power anomaly event proto of adaptive brightness. */
createAdaptiveBrightnessAnomalyEvent(boolean changeSettings)230     public static PowerAnomalyEvent createAdaptiveBrightnessAnomalyEvent(boolean changeSettings) {
231         // TODO: migrate "auto_brightness_entry" to use R.string.preference_key_auto_brightness
232         //  if we can access the Context here. (b/338314718)
233         WarningBannerInfo.Builder warningBannerInfoBuilder =
234                 WarningBannerInfo.newBuilder()
235                         .setMainButtonDestination(DisplaySettings.class.getName())
236                         .setMainButtonSourceMetricsCategory(SettingsEnums.DISPLAY)
237                         .setMainButtonSourceHighlightKey("auto_brightness_entry");
238         if (changeSettings) {
239             warningBannerInfoBuilder
240                     .setMainButtonConfigSettingsName(Settings.System.SCREEN_BRIGHTNESS_MODE)
241                     .setMainButtonConfigSettingsValue(
242                             Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
243         }
244         return PowerAnomalyEvent.newBuilder()
245                 .setEventId("BrightnessAnomaly")
246                 .setType(PowerAnomalyType.TYPE_SETTINGS_BANNER)
247                 .setKey(PowerAnomalyKey.KEY_BRIGHTNESS)
248                 .setDismissRecordKey(PowerAnomalyKey.KEY_BRIGHTNESS.name())
249                 .setScore(1.2f)
250                 .setWarningBannerInfo(warningBannerInfoBuilder.build())
251                 .build();
252     }
253 
254     /** Create a power anomaly event proto of screen timeout. */
createScreenTimeoutAnomalyEvent()255     public static PowerAnomalyEvent createScreenTimeoutAnomalyEvent() {
256         return PowerAnomalyEvent.newBuilder()
257                 .setEventId("ScreenTimeoutAnomaly")
258                 .setType(PowerAnomalyType.TYPE_SETTINGS_BANNER)
259                 .setKey(PowerAnomalyKey.KEY_SCREEN_TIMEOUT)
260                 .setDismissRecordKey(PowerAnomalyKey.KEY_SCREEN_TIMEOUT.name())
261                 .setScore(1.1f)
262                 .setWarningBannerInfo(
263                         WarningBannerInfo.newBuilder()
264                                 .setMainButtonDestination(ScreenTimeoutSettings.class.getName())
265                                 .setMainButtonSourceMetricsCategory(SettingsEnums.SCREEN_TIMEOUT)
266                                 .setMainButtonSourceHighlightKey("60000")
267                                 .build())
268                 .build();
269     }
270 
271     /** Create a power anomaly event proto of app anomaly. */
createAppAnomalyEvent()272     public static PowerAnomalyEvent createAppAnomalyEvent() {
273         return PowerAnomalyEvent.newBuilder()
274                 .setEventId("AppAnomaly")
275                 .setType(PowerAnomalyType.TYPE_APPS_ITEM)
276                 .setKey(PowerAnomalyKey.KEY_APP_TOTAL_HIGHER_THAN_USUAL)
277                 .setDismissRecordKey("KEY_APP_1")
278                 .setScore(2.0f)
279                 .setWarningItemInfo(
280                         WarningItemInfo.newBuilder()
281                                 .setStartTimestamp(1694361600000L) // 2023-09-11 00:00:00
282                                 .setEndTimestamp(1694368800000L) // 2023-09-11 02:00:00
283                                 .build())
284                 .build();
285     }
286 }
287