1 /*
2  * Copyright (C) 2020 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.internal.os;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import android.app.Service;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.ServiceConnection;
26 import android.os.BatteryConsumer;
27 import android.os.BatteryStatsManager;
28 import android.os.BatteryUsageStats;
29 import android.os.BatteryUsageStatsQuery;
30 import android.os.Binder;
31 import android.os.ConditionVariable;
32 import android.os.IBinder;
33 import android.os.Parcel;
34 import android.os.UidBatteryConsumer;
35 import android.perftests.utils.BenchmarkState;
36 import android.perftests.utils.PerfStatusReporter;
37 
38 import androidx.annotation.NonNull;
39 import androidx.annotation.Nullable;
40 import androidx.test.InstrumentationRegistry;
41 import androidx.test.filters.LargeTest;
42 import androidx.test.runner.AndroidJUnit4;
43 
44 import org.junit.Rule;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 
48 import java.util.List;
49 
50 @RunWith(AndroidJUnit4.class)
51 @LargeTest
52 public class BatteryUsageStatsPerfTest {
53 
54     @Rule
55     public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
56 
57     /**
58      * Measures the performance of {@link BatteryStatsManager#getBatteryUsageStats()},
59      * which triggers a battery stats sync on every iteration.
60      */
61     @Test
testGetBatteryUsageStats()62     public void testGetBatteryUsageStats() {
63         final Context context = InstrumentationRegistry.getContext();
64         final BatteryStatsManager batteryStatsManager =
65                 context.getSystemService(BatteryStatsManager.class);
66 
67         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
68         while (state.keepRunning()) {
69             BatteryUsageStats batteryUsageStats = batteryStatsManager.getBatteryUsageStats(
70                     new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs(0).build());
71 
72             state.pauseTiming();
73 
74             List<UidBatteryConsumer> uidBatteryConsumers =
75                     batteryUsageStats.getUidBatteryConsumers();
76             double power = 0;
77             for (int i = 0; i < uidBatteryConsumers.size(); i++) {
78                 UidBatteryConsumer uidBatteryConsumer = uidBatteryConsumers.get(i);
79                 power += uidBatteryConsumer.getConsumedPower();
80             }
81 
82             assertThat(power).isGreaterThan(0.0);
83 
84             state.resumeTiming();
85         }
86     }
87 
88     private final ConditionVariable mServiceConnected = new ConditionVariable();
89     private IBinder mService;
90 
91     private final ServiceConnection mConnection = new ServiceConnection() {
92         public void onServiceConnected(ComponentName name, IBinder service) {
93             mService = service;
94             mServiceConnected.open();
95         }
96 
97         public void onServiceDisconnected(ComponentName name) {
98             mService = null;
99         }
100     };
101 
102     /**
103      * Measures the performance of transferring BatteryUsageStats over a Binder.
104      */
105     @Test
testBatteryUsageStatsTransferOverBinder()106     public void testBatteryUsageStatsTransferOverBinder() throws Exception {
107         final Context context = InstrumentationRegistry.getContext();
108         context.bindService(
109                 new Intent(context, BatteryUsageStatsService.class),
110                 mConnection, Context.BIND_AUTO_CREATE);
111         mServiceConnected.block(30000);
112         assertThat(mService).isNotNull();
113 
114         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
115         while (state.keepRunning()) {
116             final Parcel data = Parcel.obtain();
117             final Parcel reply = Parcel.obtain();
118             mService.transact(42, data, reply, 0);
119             final BatteryUsageStats batteryUsageStats =
120                     BatteryUsageStats.CREATOR.createFromParcel(reply);
121             reply.recycle();
122             data.recycle();
123 
124             state.pauseTiming();
125 
126             assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000);
127             assertThat(batteryUsageStats.getUidBatteryConsumers()).hasSize(1000);
128             final UidBatteryConsumer uidBatteryConsumer =
129                     batteryUsageStats.getUidBatteryConsumers().get(0);
130             assertThat(uidBatteryConsumer.getConsumedPower(1)).isEqualTo(123);
131 
132             state.resumeTiming();
133         }
134 
135         context.unbindService(mConnection);
136     }
137 
138     /* This service runs in a separate process */
139     public static class BatteryUsageStatsService extends Service {
140         private final BatteryUsageStats mBatteryUsageStats;
141 
BatteryUsageStatsService()142         public BatteryUsageStatsService() {
143             mBatteryUsageStats = buildBatteryUsageStats();
144         }
145 
146         @Nullable
147         @Override
onBind(Intent intent)148         public IBinder onBind(Intent intent) {
149             return new Binder() {
150                 @Override
151                 protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
152                         int flags) {
153                     mBatteryUsageStats.writeToParcel(reply, 0);
154                     return true;
155                 }
156             };
157         }
158     }
159 
160     private static BatteryUsageStats buildBatteryUsageStats() {
161         final BatteryUsageStats.Builder builder =
162                 new BatteryUsageStats.Builder(new String[]{"FOO"}, true, false, 0)
163                         .setBatteryCapacity(4000)
164                         .setDischargePercentage(20)
165                         .setDischargedPowerRange(1000, 2000)
166                         .setStatsStartTimestamp(1000)
167                         .setStatsEndTimestamp(3000);
168 
169         builder.getAggregateBatteryConsumerBuilder(
170                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
171                 .setConsumedPower(123)
172                 .setConsumedPower(
173                         BatteryConsumer.POWER_COMPONENT_CPU, 10100)
174                 .setConsumedPowerForCustomComponent(
175                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200)
176                 .setUsageDurationMillis(
177                         BatteryConsumer.POWER_COMPONENT_CPU, 10300)
178                 .setUsageDurationForCustomComponentMillis(
179                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400);
180 
181         for (int i = 0; i < 1000; i++) {
182             final UidBatteryConsumer.Builder consumerBuilder =
183                     builder.getOrCreateUidBatteryConsumerBuilder(i)
184                             .setPackageWithHighestDrain("example.packagename" + i)
185                             .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, i * 2000)
186                             .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, i * 1000);
187             for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
188                     componentId++) {
189                 consumerBuilder.setConsumedPower(componentId, componentId * 123.0,
190                         BatteryConsumer.POWER_MODEL_POWER_PROFILE);
191                 consumerBuilder.setUsageDurationMillis(componentId, componentId * 1000);
192             }
193 
194             consumerBuilder.setConsumedPowerForCustomComponent(
195                     BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 1234)
196                     .setUsageDurationForCustomComponentMillis(
197                             BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 4321);
198         }
199         return builder.build();
200     }
201 }
202