• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.fuelgauge;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.common.truth.Truth.assertWithMessage;
21 
22 import static org.mockito.ArgumentMatchers.anyBoolean;
23 import static org.mockito.ArgumentMatchers.anyInt;
24 import static org.mockito.ArgumentMatchers.anyLong;
25 import static org.mockito.ArgumentMatchers.anyString;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.any;
28 import static org.mockito.Mockito.doAnswer;
29 import static org.mockito.Mockito.doReturn;
30 import static org.mockito.Mockito.mock;
31 import static org.mockito.Mockito.never;
32 import static org.mockito.Mockito.spy;
33 import static org.mockito.Mockito.times;
34 import static org.mockito.Mockito.verify;
35 import static org.mockito.Mockito.when;
36 
37 import android.content.Context;
38 import android.content.Intent;
39 import android.os.BatteryManager;
40 import android.os.BatteryStats;
41 import android.os.BatteryUsageStats;
42 import android.os.SystemClock;
43 import android.os.SystemProperties;
44 import android.provider.Settings;
45 import android.util.SparseIntArray;
46 
47 import com.android.internal.os.BatteryStatsHistoryIterator;
48 import com.android.settings.testutils.BatteryTestUtils;
49 import com.android.settings.testutils.FakeFeatureFactory;
50 import com.android.settings.widget.UsageView;
51 import com.android.settingslib.fuelgauge.Estimate;
52 import com.android.settingslib.utils.PowerUtil;
53 
54 import org.junit.After;
55 import org.junit.Before;
56 import org.junit.Ignore;
57 import org.junit.Test;
58 import org.junit.runner.RunWith;
59 import org.mockito.ArgumentCaptor;
60 import org.mockito.Mock;
61 import org.mockito.MockitoAnnotations;
62 import org.robolectric.RobolectricTestRunner;
63 import org.robolectric.RuntimeEnvironment;
64 
65 import java.time.Duration;
66 import java.time.Instant;
67 import java.util.Locale;
68 import java.util.Map;
69 import java.util.TimeZone;
70 import java.util.concurrent.TimeUnit;
71 
72 @RunWith(RobolectricTestRunner.class)
73 public class BatteryInfoTest {
74 
75     private static final String STATUS_CHARGING_NO_TIME = "50% - charging";
76     private static final String STATUS_CHARGING_TIME = "50% - 0 min left until full";
77     private static final String STATUS_NOT_CHARGING = "Not charging";
78     private static final String STATUS_CHARGING_FUTURE_BYPASS = "50% - Charging";
79     private static final String STATUS_CHARGING_PAUSED =
80             "50% - Charging on hold to protect battery";
81     private static final long REMAINING_TIME_NULL = -1;
82     private static final long REMAINING_TIME = 2;
83     // Strings are defined in frameworks/base/packages/SettingsLib/res/values/strings.xml
84     private static final String ENHANCED_STRING_SUFFIX = "based on your usage";
85     private static final String BATTERY_RUN_OUT_PREFIX = "Battery may run out by";
86     private static final long TEST_CHARGE_TIME_REMAINING = TimeUnit.MINUTES.toMicros(1);
87     private static final String TEST_CHARGE_TIME_REMAINING_STRINGIFIED = "1 min left until full";
88     private static final String TEST_BATTERY_LEVEL_10 = "10%";
89     private static final String FIFTEEN_MIN_FORMATTED = "15 min";
90     private static final Estimate MOCK_ESTIMATE =
91             new Estimate(
92                     1000, /* estimateMillis */
93                     false, /* isBasedOnUsage */
94                     1000 /* averageDischargeTime */);
95     private static final Map<ChargingType, Integer> CHARGING_TYPE_MAP =
96             Map.of(
97                     ChargingType.WIRED, BatteryManager.BATTERY_PLUGGED_AC,
98                     ChargingType.WIRELESS, BatteryManager.BATTERY_PLUGGED_WIRELESS,
99                     ChargingType.DOCKED, BatteryManager.BATTERY_PLUGGED_DOCK,
100                     ChargingType.NONE, 0);
101     private static final Map<ChargingSpeed, Integer> CHARGING_SPEED_MAP =
102             Map.of(
103                     ChargingSpeed.FAST, 1501000,
104                     ChargingSpeed.REGULAR, 1500000,
105                     ChargingSpeed.SLOW, 999999);
106     private static final long UNUSED_TIME_MS = -1L;
107 
108     private Intent mDisChargingBatteryBroadcast;
109     private Intent mChargingBatteryBroadcast;
110     private Context mContext;
111     private FakeFeatureFactory mFeatureFactory;
112     private TimeZone mOriginalTimeZone;
113 
114     @Mock private BatteryUsageStats mBatteryUsageStats;
115 
116     @Before
setUp()117     public void setUp() {
118         MockitoAnnotations.initMocks(this);
119         mContext = spy(RuntimeEnvironment.application);
120         mFeatureFactory = FakeFeatureFactory.setupForTest();
121 
122         mDisChargingBatteryBroadcast = BatteryTestUtils.getDischargingIntent();
123 
124         mChargingBatteryBroadcast = BatteryTestUtils.getChargingIntent();
125 
126         doReturn(false).when(mFeatureFactory.powerUsageFeatureProvider).isExtraDefend();
127         Settings.Global.putInt(
128                 mContext.getContentResolver(),
129                 BatteryUtils.SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS,
130                 0);
131 
132         // Reset static cache for testing purpose.
133         com.android.settingslib.fuelgauge.BatteryUtils.setChargingStringV2Enabled(null);
134 
135         mOriginalTimeZone = TimeZone.getDefault();
136     }
137 
138     @After
tearDown()139     public void tearDown() throws Exception {
140         TimeZone.setDefault(mOriginalTimeZone);
141     }
142 
143     @Test
getBatteryInfo_hasStatusLabel()144     public void getBatteryInfo_hasStatusLabel() {
145         doReturn(REMAINING_TIME_NULL).when(mBatteryUsageStats).getBatteryTimeRemainingMs();
146         BatteryInfo info =
147                 BatteryInfo.getBatteryInfoOld(
148                         mContext,
149                         mDisChargingBatteryBroadcast,
150                         mBatteryUsageStats,
151                         SystemClock.elapsedRealtime() * 1000,
152                         true /* shortString */);
153 
154         assertThat(info.statusLabel).isEqualTo(STATUS_NOT_CHARGING);
155     }
156 
157     @Test
getBatteryInfo_doNotShowChargingMethod_hasRemainingTime()158     public void getBatteryInfo_doNotShowChargingMethod_hasRemainingTime() {
159         doReturn(REMAINING_TIME).when(mBatteryUsageStats).getChargeTimeRemainingMs();
160         BatteryInfo info =
161                 BatteryInfo.getBatteryInfoOld(
162                         mContext,
163                         mChargingBatteryBroadcast,
164                         mBatteryUsageStats,
165                         SystemClock.elapsedRealtime() * 1000,
166                         false /* shortString */);
167 
168         assertThat(info.chargeLabel.toString()).isEqualTo(STATUS_CHARGING_TIME);
169     }
170 
171     @Test
getBatteryInfo_doNotShowChargingMethod_noRemainingTime()172     public void getBatteryInfo_doNotShowChargingMethod_noRemainingTime() {
173         doReturn(REMAINING_TIME_NULL).when(mBatteryUsageStats).getChargeTimeRemainingMs();
174         BatteryInfo info =
175                 BatteryInfo.getBatteryInfoOld(
176                         mContext,
177                         mChargingBatteryBroadcast,
178                         mBatteryUsageStats,
179                         SystemClock.elapsedRealtime() * 1000,
180                         false /* shortString */);
181 
182         assertThat(info.chargeLabel.toString()).ignoringCase().isEqualTo(STATUS_CHARGING_NO_TIME);
183     }
184 
185     @Test
getBatteryInfo_pluggedInUsingShortString_usesCorrectData()186     public void getBatteryInfo_pluggedInUsingShortString_usesCorrectData() {
187         doReturn(TEST_CHARGE_TIME_REMAINING / 1000)
188                 .when(mBatteryUsageStats)
189                 .getChargeTimeRemainingMs();
190         BatteryInfo info =
191                 BatteryInfo.getBatteryInfoOld(
192                         mContext,
193                         mChargingBatteryBroadcast,
194                         mBatteryUsageStats,
195                         SystemClock.elapsedRealtime() * 1000,
196                         true /* shortString */);
197 
198         assertThat(info.discharging).isEqualTo(false);
199         assertThat(info.chargeLabel.toString()).isEqualTo("50% - 1 min left until full");
200     }
201 
202     @Test
getBatteryInfo_basedOnUsageTrueMoreThanFifteenMinutes_usesCorrectString()203     public void getBatteryInfo_basedOnUsageTrueMoreThanFifteenMinutes_usesCorrectString() {
204         Estimate estimate =
205                 new Estimate(
206                         Duration.ofHours(4).toMillis(),
207                         true /* isBasedOnUsage */,
208                         1000 /* averageDischargeTime */);
209         BatteryInfo info =
210                 BatteryInfo.getBatteryInfo(
211                         mContext,
212                         mDisChargingBatteryBroadcast,
213                         mBatteryUsageStats,
214                         estimate,
215                         SystemClock.elapsedRealtime() * 1000,
216                         false /* shortString */);
217         BatteryInfo info2 =
218                 BatteryInfo.getBatteryInfo(
219                         mContext,
220                         mDisChargingBatteryBroadcast,
221                         mBatteryUsageStats,
222                         estimate,
223                         SystemClock.elapsedRealtime() * 1000,
224                         true /* shortString */);
225 
226         // Both long and short strings should not have extra text
227         assertThat(info.remainingLabel.toString()).doesNotContain(ENHANCED_STRING_SUFFIX);
228         assertThat(info.suggestionLabel).contains(BATTERY_RUN_OUT_PREFIX);
229         assertThat(info2.remainingLabel.toString()).doesNotContain(ENHANCED_STRING_SUFFIX);
230         assertThat(info2.suggestionLabel).contains(BATTERY_RUN_OUT_PREFIX);
231     }
232 
233     @Test
234     @Ignore
getBatteryInfo_MoreThanOneDay_suggestionLabelIsCorrectString()235     public void getBatteryInfo_MoreThanOneDay_suggestionLabelIsCorrectString() {
236         Estimate estimate =
237                 new Estimate(
238                         Duration.ofDays(3).toMillis(),
239                         true /* isBasedOnUsage */,
240                         1000 /* averageDischargeTime */);
241         BatteryInfo info =
242                 BatteryInfo.getBatteryInfo(
243                         mContext,
244                         mDisChargingBatteryBroadcast,
245                         mBatteryUsageStats,
246                         estimate,
247                         SystemClock.elapsedRealtime() * 1000,
248                         false /* shortString */);
249 
250         assertThat(info.suggestionLabel).doesNotContain(BATTERY_RUN_OUT_PREFIX);
251     }
252 
253     @Test
getBatteryInfo_basedOnUsageFalse_usesDefaultString()254     public void getBatteryInfo_basedOnUsageFalse_usesDefaultString() {
255         BatteryInfo info =
256                 BatteryInfo.getBatteryInfo(
257                         mContext,
258                         mDisChargingBatteryBroadcast,
259                         mBatteryUsageStats,
260                         MOCK_ESTIMATE,
261                         SystemClock.elapsedRealtime() * 1000,
262                         false /* shortString */);
263         BatteryInfo info2 =
264                 BatteryInfo.getBatteryInfo(
265                         mContext,
266                         mDisChargingBatteryBroadcast,
267                         mBatteryUsageStats,
268                         MOCK_ESTIMATE,
269                         SystemClock.elapsedRealtime() * 1000,
270                         true /* shortString */);
271 
272         assertThat(info.remainingLabel.toString()).doesNotContain(ENHANCED_STRING_SUFFIX);
273         assertThat(info2.remainingLabel.toString()).doesNotContain(ENHANCED_STRING_SUFFIX);
274     }
275 
276     @Test
getBatteryInfo_charging_usesChargeTime()277     public void getBatteryInfo_charging_usesChargeTime() {
278         doReturn(TEST_CHARGE_TIME_REMAINING / 1000)
279                 .when(mBatteryUsageStats)
280                 .getChargeTimeRemainingMs();
281 
282         BatteryInfo info =
283                 BatteryInfo.getBatteryInfo(
284                         mContext,
285                         mChargingBatteryBroadcast,
286                         mBatteryUsageStats,
287                         MOCK_ESTIMATE,
288                         SystemClock.elapsedRealtime() * 1000,
289                         false /* shortString */);
290 
291         assertThat(info.remainingTimeUs).isEqualTo(TEST_CHARGE_TIME_REMAINING);
292         assertThat(info.remainingLabel.toString())
293                 .isEqualTo(TEST_CHARGE_TIME_REMAINING_STRINGIFIED);
294     }
295 
296     @Test
getBatteryInfo_pluggedInWithFullBattery_onlyShowBatteryLevel()297     public void getBatteryInfo_pluggedInWithFullBattery_onlyShowBatteryLevel() {
298         mChargingBatteryBroadcast.putExtra(BatteryManager.EXTRA_LEVEL, 100);
299 
300         BatteryInfo info =
301                 BatteryInfo.getBatteryInfo(
302                         mContext,
303                         mChargingBatteryBroadcast,
304                         mBatteryUsageStats,
305                         MOCK_ESTIMATE,
306                         SystemClock.elapsedRealtime() * 1000,
307                         false /* shortString */);
308 
309         assertThat(info.chargeLabel).isEqualTo("100%");
310     }
311 
312     @Test
getBatteryInfo_chargingWithDefender_updateChargeLabel()313     public void getBatteryInfo_chargingWithDefender_updateChargeLabel() {
314         doReturn(TEST_CHARGE_TIME_REMAINING).when(mBatteryUsageStats).getChargeTimeRemainingMs();
315         doReturn(true)
316                 .when(mFeatureFactory.powerUsageFeatureProvider)
317                 .isBatteryDefend(any(BatteryInfo.class));
318         mChargingBatteryBroadcast.putExtra(
319                 BatteryManager.EXTRA_CHARGING_STATUS,
320                 BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE);
321 
322         BatteryInfo info =
323                 BatteryInfo.getBatteryInfo(
324                         mContext,
325                         mChargingBatteryBroadcast,
326                         mBatteryUsageStats,
327                         MOCK_ESTIMATE,
328                         SystemClock.elapsedRealtime() * 1000,
329                         false /* shortString */);
330 
331         assertThat(info.isBatteryDefender).isTrue();
332         assertThat(info.chargeLabel.toString()).contains(STATUS_CHARGING_PAUSED);
333     }
334 
335     @Test
getBatteryInfo_getChargeTimeRemaining_updateSettingsGlobal()336     public void getBatteryInfo_getChargeTimeRemaining_updateSettingsGlobal() {
337         doReturn(TEST_CHARGE_TIME_REMAINING).when(mBatteryUsageStats).getChargeTimeRemainingMs();
338 
339         BatteryInfo.getBatteryInfo(
340                 mContext,
341                 mChargingBatteryBroadcast,
342                 mBatteryUsageStats,
343                 MOCK_ESTIMATE,
344                 SystemClock.elapsedRealtime() * 1000,
345                 false /* shortString */);
346 
347         assertThat(BatteryInfo.getSettingsChargeTimeRemaining(mContext))
348                 .isEqualTo(TEST_CHARGE_TIME_REMAINING);
349     }
350 
351     @Test
getBatteryInfo_differentChargeTimeRemaining_updateSettingsGlobal()352     public void getBatteryInfo_differentChargeTimeRemaining_updateSettingsGlobal() {
353         doReturn(TEST_CHARGE_TIME_REMAINING).when(mBatteryUsageStats).getChargeTimeRemainingMs();
354         final long newTimeToFull = 300L;
355         doReturn(newTimeToFull).when(mBatteryUsageStats).getChargeTimeRemainingMs();
356 
357         BatteryInfo.getBatteryInfo(
358                 mContext,
359                 mChargingBatteryBroadcast,
360                 mBatteryUsageStats,
361                 MOCK_ESTIMATE,
362                 SystemClock.elapsedRealtime() * 1000,
363                 false /* shortString */);
364 
365         assertThat(BatteryInfo.getSettingsChargeTimeRemaining(mContext)).isEqualTo(newTimeToFull);
366     }
367 
368     @Test
getBatteryInfo_dockDefenderActive_updateChargeString()369     public void getBatteryInfo_dockDefenderActive_updateChargeString() {
370         doReturn(TEST_CHARGE_TIME_REMAINING / 1000)
371                 .when(mBatteryUsageStats)
372                 .getChargeTimeRemainingMs();
373         doReturn(true).when(mFeatureFactory.powerUsageFeatureProvider).isExtraDefend();
374         doReturn(true)
375                 .when(mFeatureFactory.powerUsageFeatureProvider)
376                 .isBatteryDefend(any(BatteryInfo.class));
377         Intent intent =
378                 createBatteryIntent(
379                                 BatteryManager.BATTERY_PLUGGED_DOCK,
380                                 /* level= */ 50,
381                                 BatteryManager.BATTERY_STATUS_CHARGING)
382                         .putExtra(
383                                 BatteryManager.EXTRA_CHARGING_STATUS,
384                                 BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE);
385 
386         BatteryInfo info =
387                 BatteryInfo.getBatteryInfo(
388                         mContext,
389                         intent,
390                         mBatteryUsageStats,
391                         MOCK_ESTIMATE,
392                         SystemClock.elapsedRealtime() * 1000,
393                         false /* shortString */);
394 
395         assertThat(info.chargeLabel.toString()).contains(STATUS_CHARGING_PAUSED);
396     }
397 
398     @Test
getBatteryInfo_dockDefenderTemporarilyBypassed_updateChargeLabel()399     public void getBatteryInfo_dockDefenderTemporarilyBypassed_updateChargeLabel() {
400         doReturn(REMAINING_TIME).when(mBatteryUsageStats).getChargeTimeRemainingMs();
401         mChargingBatteryBroadcast.putExtra(
402                 BatteryManager.EXTRA_CHARGING_STATUS, BatteryManager.CHARGING_POLICY_DEFAULT);
403         Settings.Global.putInt(
404                 mContext.getContentResolver(),
405                 BatteryUtils.SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS,
406                 1);
407 
408         BatteryInfo info =
409                 BatteryInfo.getBatteryInfo(
410                         mContext,
411                         createBatteryIntent(
412                                 BatteryManager.BATTERY_PLUGGED_DOCK,
413                                 /* level= */ 50,
414                                 BatteryManager.BATTERY_STATUS_CHARGING),
415                         mBatteryUsageStats,
416                         MOCK_ESTIMATE,
417                         SystemClock.elapsedRealtime() * 1000,
418                         false /* shortString */);
419 
420         assertThat(info.chargeLabel.toString()).contains(STATUS_CHARGING_TIME);
421     }
422 
423     @Test
getBatteryInfo_dockDefenderFutureBypass_updateChargeLabel()424     public void getBatteryInfo_dockDefenderFutureBypass_updateChargeLabel() {
425         doReturn(false).when(mFeatureFactory.powerUsageFeatureProvider).isExtraDefend();
426         mChargingBatteryBroadcast.putExtra(
427                 BatteryManager.EXTRA_CHARGING_STATUS, BatteryManager.CHARGING_POLICY_DEFAULT);
428 
429         BatteryInfo info =
430                 BatteryInfo.getBatteryInfo(
431                         mContext,
432                         createBatteryIntent(
433                                 BatteryManager.BATTERY_PLUGGED_DOCK,
434                                 /* level= */ 50,
435                                 BatteryManager.BATTERY_STATUS_CHARGING),
436                         mBatteryUsageStats,
437                         MOCK_ESTIMATE,
438                         SystemClock.elapsedRealtime() * 1000,
439                         false /* shortString */);
440 
441         assertThat(info.chargeLabel.toString()).contains(STATUS_CHARGING_FUTURE_BYPASS);
442     }
443 
444     @Test
getBatteryInfo_fastCharging_updateRemainingLabelAndStatusLabel()445     public void getBatteryInfo_fastCharging_updateRemainingLabelAndStatusLabel() {
446         prepareTestGetBatteryInfoEnvironment(
447                 /* remainingTimeMs= */ Duration.ofMinutes(90).toMillis(),
448                 /* chargingStringV2Enabled= */ false);
449         Intent batteryIntent =
450                 createIntentForGetBatteryInfoTest(
451                         ChargingType.WIRED, ChargingSpeed.FAST, /* batteryLevel= */ 61);
452         var expectedStatusLabel = "Charging rapidly";
453         var expectedRemainingLabel = "1 hr, 30 min left until full";
454         var expectedChargeLabel = "61% - " + expectedRemainingLabel;
455 
456         assertGetBatteryInfo(
457                 batteryIntent,
458                 /* currentTimeMillis= */ UNUSED_TIME_MS,
459                 expectedStatusLabel,
460                 expectedRemainingLabel,
461                 expectedChargeLabel);
462     }
463 
464     @Test
getBatteryInfo_regularCharging_updateRemainingLabelAndStatusLabel()465     public void getBatteryInfo_regularCharging_updateRemainingLabelAndStatusLabel() {
466         prepareTestGetBatteryInfoEnvironment(
467                 /* remainingTimeMs= */ Duration.ofMinutes(80).toMillis(),
468                 /* chargingStringV2Enabled= */ false);
469         Intent batteryIntent =
470                 createIntentForGetBatteryInfoTest(
471                         ChargingType.WIRED, ChargingSpeed.REGULAR, /* batteryLevel= */ 33);
472         var expectedStatusLabel = "Charging";
473         var expectedRemainingLabel = "1 hr, 20 min left until full";
474         var expectedChargeLabel = "33% - " + expectedRemainingLabel;
475 
476         assertGetBatteryInfo(
477                 batteryIntent,
478                 /* currentTimeMillis= */ UNUSED_TIME_MS,
479                 expectedStatusLabel,
480                 expectedRemainingLabel,
481                 expectedChargeLabel);
482     }
483 
484     @Test
getBatteryInfo_slowCharging_updateRemainingLabelAndStatusLabel()485     public void getBatteryInfo_slowCharging_updateRemainingLabelAndStatusLabel() {
486         prepareTestGetBatteryInfoEnvironment(
487                 /* remainingTimeMs= */ Duration.ofMinutes(100).toMillis(),
488                 /* chargingStringV2Enabled= */ false);
489         Intent batteryIntent =
490                 createIntentForGetBatteryInfoTest(
491                         ChargingType.WIRED, ChargingSpeed.SLOW, /* batteryLevel= */ 53);
492         var expectedStatusLabel = "Charging slowly";
493         var expectedRemainingLabel = "1 hr, 40 min left until full";
494         var expectedChargeLabel = "53% - " + expectedRemainingLabel;
495 
496         assertGetBatteryInfo(
497                 batteryIntent,
498                 /* currentTimeMillis= */ UNUSED_TIME_MS,
499                 expectedStatusLabel,
500                 expectedRemainingLabel,
501                 expectedChargeLabel);
502     }
503 
504     @Test
getBatteryInfo_wirelessCharging_updateRemainingLabelAndStatusLabel()505     public void getBatteryInfo_wirelessCharging_updateRemainingLabelAndStatusLabel() {
506         prepareTestGetBatteryInfoEnvironment(
507                 /* remainingTimeMs= */ Duration.ofMinutes(130).toMillis(),
508                 /* chargingStringV2Enabled= */ false);
509         Intent batteryIntent =
510                 createIntentForGetBatteryInfoTest(
511                         ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 10);
512         var expectedStatusLabel = "Charging wirelessly";
513         var expectedRemainingLabel = "2 hr, 10 min left until full";
514         var expectedChargeLabel = "10% - " + expectedRemainingLabel;
515 
516         assertGetBatteryInfo(
517                 batteryIntent,
518                 /* currentTimeMillis= */ UNUSED_TIME_MS,
519                 expectedStatusLabel,
520                 expectedRemainingLabel,
521                 expectedChargeLabel);
522     }
523 
524     @Test
getBatteryInfo_dockedCharging_updateRemainingLabelAndStatusLabel()525     public void getBatteryInfo_dockedCharging_updateRemainingLabelAndStatusLabel() {
526         prepareTestGetBatteryInfoEnvironment(
527                 /* remainingTimeMs= */ Duration.ofMinutes(30).toMillis(),
528                 /* chargingStringV2Enabled= */ false);
529         Intent batteryIntent =
530                 createIntentForGetBatteryInfoTest(
531                         ChargingType.DOCKED, ChargingSpeed.REGULAR, /* batteryLevel= */ 51);
532         var expectedStatusLabel = "Charging";
533         var expectedRemainingLabel = "30 min left until full";
534         var expectedChargeLabel = "51% - " + expectedRemainingLabel;
535 
536         assertGetBatteryInfo(
537                 batteryIntent,
538                 /* currentTimeMillis= */ UNUSED_TIME_MS,
539                 expectedStatusLabel,
540                 expectedRemainingLabel,
541                 expectedChargeLabel);
542     }
543 
544     @Test
545     @Ignore
getBatteryInfo_fastChargingV2_updateRemainingLabelAndStatusLabel()546     public void getBatteryInfo_fastChargingV2_updateRemainingLabelAndStatusLabel() {
547         prepareTestGetBatteryInfoEnvironment(
548                 /* remainingTimeMs= */ Duration.ofMinutes(30).toMillis(),
549                 /* chargingStringV2Enabled= */ true);
550         Intent batteryIntent =
551                 createIntentForGetBatteryInfoTest(
552                         ChargingType.WIRED, ChargingSpeed.FAST, /* batteryLevel= */ 56);
553         var expectedStatusLabel = "Fast charging";
554         var expectedRemainingLabel = "Full by ";
555         var expectedChargeLabel = "56% - " + expectedStatusLabel + " - " + expectedRemainingLabel;
556         var currentTimeMillis = Instant.parse("2024-04-01T13:00:00Z").toEpochMilli();
557 
558         assertGetBatteryInfo(
559                 batteryIntent,
560                 currentTimeMillis,
561                 expectedStatusLabel,
562                 expectedRemainingLabel,
563                 expectedChargeLabel);
564     }
565 
566     @Test
getBatteryInfo_regularChargingV2_updateRemainingLabelAndStatusLabel()567     public void getBatteryInfo_regularChargingV2_updateRemainingLabelAndStatusLabel() {
568         prepareTestGetBatteryInfoEnvironment(
569                 /* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
570                 /* chargingStringV2Enabled= */ true);
571         Intent batteryIntent =
572                 createIntentForGetBatteryInfoTest(
573                         ChargingType.WIRED, ChargingSpeed.REGULAR, /* batteryLevel= */ 12);
574         var expectedStatusLabel = "Charging";
575         var expectedRemainingLabel = "Fully charged by ";
576         var expectedChargeLabel = "12% - " + expectedRemainingLabel;
577         var currentTimeMillis = Instant.parse("2024-04-01T13:00:00Z").toEpochMilli();
578 
579         assertGetBatteryInfo(
580                 batteryIntent,
581                 currentTimeMillis,
582                 expectedStatusLabel,
583                 expectedRemainingLabel,
584                 expectedChargeLabel);
585     }
586 
587     @Test
getBatteryInfo_slowChargingV2_updateRemainingLabelAndStatusLabel()588     public void getBatteryInfo_slowChargingV2_updateRemainingLabelAndStatusLabel() {
589         prepareTestGetBatteryInfoEnvironment(
590                 /* remainingTimeMs= */ Duration.ofHours(2).toMillis(),
591                 /* chargingStringV2Enabled= */ true);
592         Intent batteryIntent =
593                 createIntentForGetBatteryInfoTest(
594                         ChargingType.WIRED, ChargingSpeed.SLOW, /* batteryLevel= */ 18);
595         var expectedStatusLabel = "Charging";
596         var expectedRemainingLabel = "Fully charged by";
597         var expectedChargeLabel = "18% - " + expectedRemainingLabel;
598         var currentTimeMillis = Instant.parse("2024-04-01T13:00:00Z").toEpochMilli();
599 
600         assertGetBatteryInfo(
601                 batteryIntent,
602                 currentTimeMillis,
603                 expectedStatusLabel,
604                 expectedRemainingLabel,
605                 expectedChargeLabel);
606     }
607 
608     @Test
getBatteryInfo_wirelessChargingV2_updateRemainingLabelAndStatusLabel()609     public void getBatteryInfo_wirelessChargingV2_updateRemainingLabelAndStatusLabel() {
610         prepareTestGetBatteryInfoEnvironment(
611                 /* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
612                 /* chargingStringV2Enabled= */ true);
613         Intent batteryIntent =
614                 createIntentForGetBatteryInfoTest(
615                         ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 45);
616         var expectedStatusLabel = "Charging";
617         var expectedRemainingLabel = "Fully charged by";
618         var expectedChargeLabel = "45% - " + expectedRemainingLabel;
619         var currentTimeMillis = Instant.parse("2024-04-01T15:00:00Z").toEpochMilli();
620 
621         assertGetBatteryInfo(
622                 batteryIntent,
623                 currentTimeMillis,
624                 expectedStatusLabel,
625                 expectedRemainingLabel,
626                 expectedChargeLabel);
627     }
628 
629     @Test
getBatteryInfo_dockedChargingV2_updateRemainingLabelAndStatusLabel()630     public void getBatteryInfo_dockedChargingV2_updateRemainingLabelAndStatusLabel() {
631         prepareTestGetBatteryInfoEnvironment(
632                 /* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
633                 /* chargingStringV2Enabled= */ true);
634         Intent batteryIntent =
635                 createIntentForGetBatteryInfoTest(
636                         ChargingType.DOCKED, ChargingSpeed.REGULAR, /* batteryLevel= */ 66);
637         var expectedStatusLabel = "Charging";
638         var expectedRemainingLabel = "Fully charged by";
639         var expectedChargeLabel = "66% - " + expectedRemainingLabel;
640         var currentTimeMillis = Instant.parse("2021-02-09T13:00:00.00Z").toEpochMilli();
641 
642         assertGetBatteryInfo(
643                 batteryIntent,
644                 currentTimeMillis,
645                 expectedStatusLabel,
646                 expectedRemainingLabel,
647                 expectedChargeLabel);
648     }
649 
650     @Test
getBatteryInfo_customizedWLCLabel_updateRemainingLabelAndStatusLabel()651     public void getBatteryInfo_customizedWLCLabel_updateRemainingLabelAndStatusLabel() {
652         prepareTestGetBatteryInfoEnvironment(
653                 /* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
654                 /* chargingStringV2Enabled= */ true);
655         Intent batteryIntent =
656                 createIntentForGetBatteryInfoTest(
657                         ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 45);
658         var expectedLabel = "Full by 8:00 AM";
659         when(mFeatureFactory.batterySettingsFeatureProvider.getWirelessChargingRemainingLabel(
660                         eq(mContext), anyLong(), anyLong()))
661                 .thenReturn(expectedLabel);
662         var currentTimeMillis = Instant.parse("2021-02-09T13:00:00.00Z").toEpochMilli();
663         var info =
664                 BatteryInfo.getBatteryInfo(
665                         mContext,
666                         batteryIntent,
667                         mBatteryUsageStats,
668                         MOCK_ESTIMATE,
669                         /* elapsedRealtimeUs= */ UNUSED_TIME_MS,
670                         /* shortString= */ false,
671                         /* currentTimeMillis= */ currentTimeMillis);
672 
673         assertThat(info.remainingLabel).isEqualTo(expectedLabel);
674     }
675 
676     @Test
getBatteryInfo_noCustomizedWLCLabel_updateRemainingLabelAndStatusLabel()677     public void getBatteryInfo_noCustomizedWLCLabel_updateRemainingLabelAndStatusLabel() {
678         prepareTestGetBatteryInfoEnvironment(
679                 /* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
680                 /* chargingStringV2Enabled= */ true);
681         Intent batteryIntent =
682                 createIntentForGetBatteryInfoTest(
683                         ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 45);
684         when(mFeatureFactory.batterySettingsFeatureProvider.getWirelessChargingRemainingLabel(
685                         eq(mContext), anyLong(), anyLong()))
686                 .thenReturn(null);
687         var expectedStatusLabel = "Charging";
688         var expectedRemainingLabel = "Fully charged by";
689         var expectedChargeLabel = "45% - " + expectedRemainingLabel;
690         var currentTimeMillis = Instant.parse("2024-04-01T15:00:00Z").toEpochMilli();
691 
692         assertGetBatteryInfo(
693                 batteryIntent,
694                 currentTimeMillis,
695                 expectedStatusLabel,
696                 expectedRemainingLabel,
697                 expectedChargeLabel);
698     }
699 
700     @Test
getBatteryInfo_noCustomWirelessChargingLabelWithV1_updateRemainingAndStatusLabel()701     public void getBatteryInfo_noCustomWirelessChargingLabelWithV1_updateRemainingAndStatusLabel() {
702         prepareTestGetBatteryInfoEnvironment(
703                 /* remainingTimeMs= */ Duration.ofMinutes(130).toMillis(),
704                 /* chargingStringV2Enabled= */ false);
705         Intent batteryIntent =
706                 createIntentForGetBatteryInfoTest(
707                         ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 10);
708         when(mFeatureFactory.batterySettingsFeatureProvider.getWirelessChargingRemainingLabel(
709                         eq(mContext), anyLong(), anyLong()))
710                 .thenReturn(null);
711         var expectedStatusLabel = "Charging wirelessly";
712         var expectedRemainingLabel = "2 hr, 10 min left until full";
713         var expectedChargeLabel = "10% - " + expectedRemainingLabel;
714 
715         assertGetBatteryInfo(
716                 batteryIntent,
717                 /* currentTimeMillis= */ UNUSED_TIME_MS,
718                 expectedStatusLabel,
719                 expectedRemainingLabel,
720                 expectedChargeLabel);
721     }
722 
723     @Test
getBatteryInfo_chargeOptimizationMode_updateRemainingAndStatusLabel()724     public void getBatteryInfo_chargeOptimizationMode_updateRemainingAndStatusLabel() {
725         prepareTestGetBatteryInfoEnvironment(
726                 /* remainingTimeMs= */ Duration.ofMinutes(130).toMillis(),
727                 /* chargingStringV2Enabled= */ false);
728         Intent batteryIntent =
729                 createIntentForGetBatteryInfoTest(
730                         ChargingType.WIRED, ChargingSpeed.REGULAR, /* batteryLevel= */ 65);
731         var expectedRemainingLabel = "Expected remaining label";
732         var expectedChargeLabel = "65% - " + expectedRemainingLabel;
733         when(mFeatureFactory.batterySettingsFeatureProvider.isChargingOptimizationMode(
734                         eq(mContext), anyBoolean()))
735                 .thenReturn(true);
736         when(mFeatureFactory.batterySettingsFeatureProvider.getChargingOptimizationRemainingLabel(
737                         eq(mContext), anyInt(), anyInt(), anyLong(), anyLong()))
738                 .thenReturn(expectedRemainingLabel);
739         when(mFeatureFactory.batterySettingsFeatureProvider.getChargingOptimizationChargeLabel(
740                         eq(mContext), anyInt(), anyString(), anyLong(), anyLong()))
741                 .thenReturn(expectedChargeLabel);
742         var expectedStatusLabel = "Charging";
743 
744         assertGetBatteryInfo(
745                 batteryIntent,
746                 /* currentTimeMillis= */ UNUSED_TIME_MS,
747                 expectedStatusLabel,
748                 expectedRemainingLabel,
749                 expectedChargeLabel);
750     }
751 
752     @Test
getBatteryInfo_notChargeOptimizationModeWithV1_updateRemainingAndStatusLabel()753     public void getBatteryInfo_notChargeOptimizationModeWithV1_updateRemainingAndStatusLabel() {
754         prepareTestGetBatteryInfoEnvironment(
755                 /* remainingTimeMs= */ Duration.ofMinutes(130).toMillis(),
756                 /* chargingStringV2Enabled= */ false);
757         Intent batteryIntent =
758                 createIntentForGetBatteryInfoTest(
759                         ChargingType.WIRED, ChargingSpeed.REGULAR, /* batteryLevel= */ 65);
760         when(mFeatureFactory.batterySettingsFeatureProvider.isChargingOptimizationMode(
761                         eq(mContext), anyBoolean()))
762                 .thenReturn(false);
763         var expectedStatusLabel = "Charging";
764         var expectedRemainingLabel = "2 hr, 10 min left until full";
765         var expectedChargeLabel = "65% - " + expectedRemainingLabel;
766 
767         assertGetBatteryInfo(
768                 batteryIntent,
769                 /* currentTimeMillis= */ UNUSED_TIME_MS,
770                 expectedStatusLabel,
771                 expectedRemainingLabel,
772                 expectedChargeLabel);
773     }
774 
775     @Test
getBatteryInfo_notChargeOptimizationModeWithV2_updateRemainingAndStatusLabel()776     public void getBatteryInfo_notChargeOptimizationModeWithV2_updateRemainingAndStatusLabel() {
777         prepareTestGetBatteryInfoEnvironment(
778                 /* remainingTimeMs= */ Duration.ofMinutes(130).toMillis(),
779                 /* chargingStringV2Enabled= */ true);
780         Intent batteryIntent =
781                 createIntentForGetBatteryInfoTest(
782                         ChargingType.WIRED, ChargingSpeed.REGULAR, /* batteryLevel= */ 65);
783         when(mFeatureFactory.batterySettingsFeatureProvider.isChargingOptimizationMode(
784                         eq(mContext), anyBoolean()))
785                 .thenReturn(false);
786         var expectedStatusLabel = "Charging";
787         var expectedRemainingLabel = "Fully charged by";
788         var expectedChargeLabel = "65% - " + expectedRemainingLabel;
789         var currentTimeMillis = Instant.parse("2024-04-01T15:00:00Z").toEpochMilli();
790 
791         assertGetBatteryInfo(
792                 batteryIntent,
793                 currentTimeMillis,
794                 expectedStatusLabel,
795                 expectedRemainingLabel,
796                 expectedChargeLabel);
797     }
798 
799     @Test
getBatteryInfo_longlife_shouldSetLonglife()800     public void getBatteryInfo_longlife_shouldSetLonglife() {
801         var batteryIntent = createIntentForLongLifeTest(/* hasLongLife= */ true);
802 
803         var batteryInfo =
804                 BatteryInfo.getBatteryInfo(
805                         mContext,
806                         batteryIntent,
807                         mBatteryUsageStats,
808                         /* estimate= */ MOCK_ESTIMATE,
809                         /* elapsedRealtimeUs= */ 0L,
810                         /* shortString= */ false,
811                         /* currentTimeMs= */ 0L);
812 
813         assertThat(batteryInfo.isLongLife).isTrue();
814     }
815 
816     @Test
getBatteryInfo_noLonglife_shouldNotLonglife()817     public void getBatteryInfo_noLonglife_shouldNotLonglife() {
818         var batteryIntent = createIntentForLongLifeTest(/* hasLongLife= */ false);
819 
820         var batteryInfo =
821                 BatteryInfo.getBatteryInfo(
822                         mContext,
823                         batteryIntent,
824                         mBatteryUsageStats,
825                         /* estimate= */ MOCK_ESTIMATE,
826                         /* elapsedRealtimeUs= */ 0L,
827                         /* shortString= */ false,
828                         /* currentTimeMs= */ 0L);
829 
830         assertThat(batteryInfo.isLongLife).isFalse();
831     }
832 
833     @Test
getBatteryInfo_plugTypeNoneIsChargeOptimization_chargingString()834     public void getBatteryInfo_plugTypeNoneIsChargeOptimization_chargingString() {
835         prepareTestGetBatteryInfoEnvironment(
836                 /* remainingTimeMs= */ Duration.ofMinutes(130).toMillis(),
837                 /* chargingStringV2Enabled= */ false);
838         Intent batteryIntent =
839                 createIntentForGetBatteryInfoTest(
840                         ChargingType.NONE,
841                         ChargingSpeed.REGULAR,
842                         /* batteryLevel= */ 85,
843                         BatteryManager.BATTERY_STATUS_DISCHARGING,
844                         /* isLonglife= */ true);
845         var expectedRemainingLabel = "Expected remaining label";
846         var expectedChargeLabel = "85% - " + expectedRemainingLabel;
847         when(mFeatureFactory.batterySettingsFeatureProvider.isChargingOptimizationMode(
848                         eq(mContext), anyBoolean()))
849                 .thenReturn(true);
850         when(mFeatureFactory.batterySettingsFeatureProvider.getChargingOptimizationRemainingLabel(
851                         eq(mContext), anyInt(), anyInt(), anyLong(), anyLong()))
852                 .thenReturn(expectedRemainingLabel);
853         when(mFeatureFactory.batterySettingsFeatureProvider.getChargingOptimizationChargeLabel(
854                         eq(mContext), anyInt(), anyString(), anyLong(), anyLong()))
855                 .thenReturn(expectedChargeLabel);
856         var expectedStatusLabel = "Not charging";
857 
858         assertGetBatteryInfo(
859                 batteryIntent,
860                 /* currentTimeMillis= */ UNUSED_TIME_MS,
861                 expectedStatusLabel,
862                 expectedRemainingLabel,
863                 expectedChargeLabel);
864     }
865 
866     @Test
getBatteryInfo_plugTypeNoneNotChargeOptimization_dischargingString()867     public void getBatteryInfo_plugTypeNoneNotChargeOptimization_dischargingString() {
868         prepareTestGetBatteryInfoEnvironment(
869                 /* remainingTimeMs= */ Duration.ofMinutes(130).toMillis(),
870                 /* chargingStringV2Enabled= */ false);
871         Intent batteryIntent =
872                 createIntentForGetBatteryInfoTest(
873                         ChargingType.NONE,
874                         ChargingSpeed.REGULAR,
875                         /* batteryLevel= */ 85,
876                         BatteryManager.BATTERY_STATUS_DISCHARGING,
877                         /* isLonglife= */ false);
878         var expectedRemainingLabel =
879                 PowerUtil.getBatteryRemainingShortStringFormatted(
880                         mContext, PowerUtil.convertUsToMs(1000L));
881         when(mFeatureFactory.batterySettingsFeatureProvider.isChargingOptimizationMode(
882                         eq(mContext), anyBoolean()))
883                 .thenReturn(false);
884         var expectedStatusLabel = "Not charging";
885 
886         assertGetBatteryInfo(
887                 batteryIntent,
888                 /* currentTimeMillis= */ UNUSED_TIME_MS,
889                 expectedStatusLabel,
890                 expectedRemainingLabel,
891                 expectedRemainingLabel);
892     }
893 
894     private enum ChargingSpeed {
895         FAST,
896         REGULAR,
897         SLOW
898     }
899 
900     private enum ChargingType {
901         WIRED,
902         WIRELESS,
903         DOCKED,
904         NONE
905     }
906 
createIntentForLongLifeTest(Boolean hasLongLife)907     private static Intent createIntentForLongLifeTest(Boolean hasLongLife) {
908         return new Intent(Intent.ACTION_BATTERY_CHANGED)
909                 .putExtra(
910                         BatteryManager.EXTRA_CHARGING_STATUS,
911                         hasLongLife
912                                 ? BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE
913                                 : BatteryManager.CHARGING_POLICY_DEFAULT);
914     }
915 
createIntentForGetBatteryInfoTest( ChargingType chargingType, ChargingSpeed chargingSpeed, int batteryLevel)916     private static Intent createIntentForGetBatteryInfoTest(
917             ChargingType chargingType, ChargingSpeed chargingSpeed, int batteryLevel) {
918         return createIntentForGetBatteryInfoTest(
919                 chargingType,
920                 chargingSpeed,
921                 batteryLevel,
922                 BatteryManager.BATTERY_STATUS_CHARGING,
923                 /* isLonglife= */ false);
924     }
925 
createIntentForGetBatteryInfoTest( ChargingType chargingType, ChargingSpeed chargingSpeed, int batteryLevel, int chargingStatus, boolean isLonglife)926     private static Intent createIntentForGetBatteryInfoTest(
927             ChargingType chargingType,
928             ChargingSpeed chargingSpeed,
929             int batteryLevel,
930             int chargingStatus,
931             boolean isLonglife) {
932         return createBatteryIntent(
933                         CHARGING_TYPE_MAP.get(chargingType), batteryLevel, chargingStatus)
934                 .putExtra(
935                         BatteryManager.EXTRA_MAX_CHARGING_CURRENT,
936                         CHARGING_SPEED_MAP.get(chargingSpeed))
937                 .putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, 5000000)
938                 .putExtra(
939                         BatteryManager.EXTRA_CHARGING_STATUS,
940                         isLonglife
941                                 ? BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE
942                                 : BatteryManager.CHARGING_POLICY_DEFAULT);
943     }
944 
prepareTestGetBatteryInfoEnvironment( long remainingTimeMs, boolean chargingStringV2Enabled)945     private void prepareTestGetBatteryInfoEnvironment(
946             long remainingTimeMs, boolean chargingStringV2Enabled) {
947         when(mBatteryUsageStats.getChargeTimeRemainingMs()).thenReturn(remainingTimeMs);
948         SystemProperties.set(
949                 com.android.settingslib.fuelgauge.BatteryUtils.PROPERTY_CHARGING_STRING_V2_KEY,
950                 String.valueOf(chargingStringV2Enabled));
951         Settings.Global.putInt(
952                 mContext.getContentResolver(),
953                 BatteryUtils.SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS,
954                 1);
955     }
956 
assertGetBatteryInfo( Intent batteryIntent, long currentTimeMillis, String expectedStatusLabel, String expectedRemainingLabel, String expectedChargeLabel)957     private void assertGetBatteryInfo(
958             Intent batteryIntent,
959             long currentTimeMillis,
960             String expectedStatusLabel,
961             String expectedRemainingLabel,
962             String expectedChargeLabel) {
963         mContext.getResources().getConfiguration().setLocale(Locale.US);
964         TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
965         var info =
966                 BatteryInfo.getBatteryInfo(
967                         mContext,
968                         batteryIntent,
969                         mBatteryUsageStats,
970                         MOCK_ESTIMATE,
971                         /* elapsedRealtimeUs= */ UNUSED_TIME_MS,
972                         /* shortString= */ false,
973                         /* currentTimeMillis= */ currentTimeMillis);
974 
975         assertWithMessage("statusLabel is incorrect")
976                 .that(info.statusLabel)
977                 .isEqualTo(expectedStatusLabel);
978         assertWithMessage("remainingLabel is incorrect")
979                 .that(info.remainingLabel.toString())
980                 .contains(expectedRemainingLabel);
981         assertWithMessage("chargeLabel is incorrect")
982                 .that(info.chargeLabel.toString())
983                 .contains(expectedChargeLabel);
984     }
985 
createBatteryIntent(int plugged, int level, int status)986     private static Intent createBatteryIntent(int plugged, int level, int status) {
987         return new Intent()
988                 .putExtra(BatteryManager.EXTRA_PLUGGED, plugged)
989                 .putExtra(BatteryManager.EXTRA_LEVEL, level)
990                 .putExtra(BatteryManager.EXTRA_SCALE, 100)
991                 .putExtra(BatteryManager.EXTRA_STATUS, status);
992     }
993 
994     // Make our battery stats return a sequence of battery events.
mockBatteryStatsHistory()995     private void mockBatteryStatsHistory() {
996         // Mock out new data every time iterateBatteryStatsHistory is called.
997         doAnswer(
998                         invocation -> {
999                             BatteryStatsHistoryIterator iterator =
1000                                     mock(BatteryStatsHistoryIterator.class);
1001                             when(iterator.next())
1002                                     .thenReturn(
1003                                             makeHistoryIterm(1000, 99),
1004                                             makeHistoryIterm(1500, 98),
1005                                             makeHistoryIterm(2000, 97),
1006                                             null);
1007                             return iterator;
1008                         })
1009                 .when(mBatteryUsageStats)
1010                 .iterateBatteryStatsHistory();
1011     }
1012 
makeHistoryIterm(long time, int batteryLevel)1013     private BatteryStats.HistoryItem makeHistoryIterm(long time, int batteryLevel) {
1014         BatteryStats.HistoryItem record = new BatteryStats.HistoryItem();
1015         record.cmd = BatteryStats.HistoryItem.CMD_UPDATE;
1016         record.time = time;
1017         record.batteryLevel = (byte) batteryLevel;
1018         return record;
1019     }
1020 
assertOnlyHistory(BatteryInfo info)1021     private void assertOnlyHistory(BatteryInfo info) {
1022         mockBatteryStatsHistory();
1023         UsageView view = mock(UsageView.class);
1024         when(view.getContext()).thenReturn(mContext);
1025 
1026         info.bindHistory(view);
1027         verify(view, times(1)).configureGraph(anyInt(), anyInt());
1028         verify(view, times(1)).addPath(any(SparseIntArray.class));
1029         verify(view, never()).addProjectedPath(any(SparseIntArray.class));
1030     }
1031 
assertHistoryAndLinearProjection(BatteryInfo info)1032     private void assertHistoryAndLinearProjection(BatteryInfo info) {
1033         mockBatteryStatsHistory();
1034         UsageView view = mock(UsageView.class);
1035         when(view.getContext()).thenReturn(mContext);
1036 
1037         info.bindHistory(view);
1038         verify(view, times(2)).configureGraph(anyInt(), anyInt());
1039         verify(view, times(1)).addPath(any(SparseIntArray.class));
1040         ArgumentCaptor<SparseIntArray> pointsActual = ArgumentCaptor.forClass(SparseIntArray.class);
1041         verify(view, times(1)).addProjectedPath(pointsActual.capture());
1042 
1043         // Check that we have two points and the first is correct.
1044         assertThat(pointsActual.getValue().size()).isEqualTo(2);
1045         assertThat(pointsActual.getValue().keyAt(0)).isEqualTo(2000);
1046         assertThat(pointsActual.getValue().valueAt(0)).isEqualTo(97);
1047     }
1048 
assertHistoryAndEnhancedProjection(BatteryInfo info)1049     private void assertHistoryAndEnhancedProjection(BatteryInfo info) {
1050         mockBatteryStatsHistory();
1051         UsageView view = mock(UsageView.class);
1052         when(view.getContext()).thenReturn(mContext);
1053         SparseIntArray pointsExpected = new SparseIntArray();
1054         pointsExpected.append(2000, 96);
1055         pointsExpected.append(2500, 95);
1056         pointsExpected.append(3000, 94);
1057         doReturn(pointsExpected)
1058                 .when(mFeatureFactory.powerUsageFeatureProvider)
1059                 .getEnhancedBatteryPredictionCurve(any(Context.class), anyLong());
1060 
1061         info.bindHistory(view);
1062         verify(view, times(2)).configureGraph(anyInt(), anyInt());
1063         verify(view, times(1)).addPath(any(SparseIntArray.class));
1064         ArgumentCaptor<SparseIntArray> pointsActual = ArgumentCaptor.forClass(SparseIntArray.class);
1065         verify(view, times(1)).addProjectedPath(pointsActual.capture());
1066         assertThat(pointsActual.getValue()).isEqualTo(pointsExpected);
1067     }
1068 
getBatteryInfo(boolean charging, boolean enhanced, boolean estimate)1069     private BatteryInfo getBatteryInfo(boolean charging, boolean enhanced, boolean estimate) {
1070         if (charging && estimate) {
1071             doReturn(1000L).when(mBatteryUsageStats).getChargeTimeRemainingMs();
1072         } else {
1073             doReturn(0L).when(mBatteryUsageStats).getChargeTimeRemainingMs();
1074         }
1075         Estimate batteryEstimate =
1076                 new Estimate(
1077                         estimate ? 1000 : 0,
1078                         false /* isBasedOnUsage */,
1079                         1000 /* averageDischargeTime */);
1080         BatteryInfo info =
1081                 BatteryInfo.getBatteryInfo(
1082                         mContext,
1083                         charging ? mChargingBatteryBroadcast : mDisChargingBatteryBroadcast,
1084                         mBatteryUsageStats,
1085                         batteryEstimate,
1086                         SystemClock.elapsedRealtime() * 1000,
1087                         false);
1088         doReturn(enhanced)
1089                 .when(mFeatureFactory.powerUsageFeatureProvider)
1090                 .isEnhancedBatteryPredictionEnabled(mContext);
1091         return info;
1092     }
1093 
1094     @Test
testBindHistory()1095     public void testBindHistory() {
1096         BatteryInfo info;
1097 
1098         info = getBatteryInfo(false /* charging */, false /* enhanced */, false /* estimate */);
1099         assertOnlyHistory(info);
1100 
1101         info = getBatteryInfo(false /* charging */, false /* enhanced */, true /* estimate */);
1102         assertHistoryAndLinearProjection(info);
1103 
1104         info = getBatteryInfo(false /* charging */, true /* enhanced */, false /* estimate */);
1105         assertOnlyHistory(info);
1106 
1107         info = getBatteryInfo(false /* charging */, true /* enhanced */, true /* estimate */);
1108         assertHistoryAndEnhancedProjection(info);
1109 
1110         info = getBatteryInfo(true /* charging */, false /* enhanced */, false /* estimate */);
1111         assertOnlyHistory(info);
1112 
1113         info = getBatteryInfo(true /* charging */, false /* enhanced */, true /* estimate */);
1114         assertHistoryAndLinearProjection(info);
1115 
1116         info = getBatteryInfo(true /* charging */, true /* enhanced */, false /* estimate */);
1117         assertOnlyHistory(info);
1118 
1119         // Linear projection for charging even in enhanced mode.
1120         info = getBatteryInfo(true /* charging */, true /* enhanced */, true /* estimate */);
1121         assertHistoryAndLinearProjection(info);
1122     }
1123 }
1124