Add BatteryStatsManager.getWakeLockStats() API

Bug: 200753927
Test: atest FrameworksCoreTests:BatteryStatsImplTest
Change-Id: Iafb216250e26b93cec5106d1305fa68c6f20282f
This commit is contained in:
Dmitri Plotnikov
2021-12-02 18:52:17 -08:00
parent 1cac926292
commit a633941cec
8 changed files with 276 additions and 0 deletions

View File

@@ -3373,6 +3373,11 @@ public abstract class BatteryStats implements Parcelable {
public abstract Map<String, ? extends Timer> getKernelWakelockStats();
/**
* Returns aggregated wake lock stats.
*/
public abstract WakeLockStats getWakeLockStats();
/**
* Returns Timers tracking the total time of each Resource Power Manager state and voter.
*/

View File

@@ -157,6 +157,7 @@ public final class BatteryStatsManager {
@Retention(RetentionPolicy.SOURCE)
public @interface WifiSupplState {}
private final IBatteryStats mBatteryStats;
/** @hide */
@@ -351,6 +352,21 @@ public final class BatteryStatsManager {
}
}
/**
* Retrieves accumulate wake lock stats.
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.BATTERY_STATS)
@NonNull
public WakeLockStats getWakeLockStats() {
try {
return mBatteryStats.getWakeLockStats();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Indicates an app acquiring full wifi lock.
*

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
/** {@hide} */
parcelable WakeLockStats;

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
import android.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
/**
* Snapshot of wake lock stats.
* @hide
*/
public final class WakeLockStats implements Parcelable {
/** @hide */
public static class WakeLock {
public final int uid;
@NonNull
public final String name;
public final int timesAcquired;
public final long totalTimeHeldMs;
/**
* Time in milliseconds that the lock has been held or 0 if not currently holding the lock
*/
public final long timeHeldMs;
public WakeLock(int uid, @NonNull String name, int timesAcquired, long totalTimeHeldMs,
long timeHeldMs) {
this.uid = uid;
this.name = name;
this.timesAcquired = timesAcquired;
this.totalTimeHeldMs = totalTimeHeldMs;
this.timeHeldMs = timeHeldMs;
}
private WakeLock(Parcel in) {
uid = in.readInt();
name = in.readString();
timesAcquired = in.readInt();
totalTimeHeldMs = in.readLong();
timeHeldMs = in.readLong();
}
private void writeToParcel(Parcel out) {
out.writeInt(uid);
out.writeString(name);
out.writeInt(timesAcquired);
out.writeLong(totalTimeHeldMs);
out.writeLong(timeHeldMs);
}
@Override
public String toString() {
return "WakeLock{"
+ "uid=" + uid
+ ", name='" + name + '\''
+ ", timesAcquired=" + timesAcquired
+ ", totalTimeHeldMs=" + totalTimeHeldMs
+ ", timeHeldMs=" + timeHeldMs
+ '}';
}
}
private final List<WakeLock> mWakeLocks;
/** @hide **/
public WakeLockStats(@NonNull List<WakeLock> wakeLocks) {
mWakeLocks = wakeLocks;
}
@NonNull
public List<WakeLock> getWakeLocks() {
return mWakeLocks;
}
private WakeLockStats(Parcel in) {
final int size = in.readInt();
mWakeLocks = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
mWakeLocks.add(new WakeLock(in));
}
}
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
final int size = mWakeLocks.size();
out.writeInt(size);
for (int i = 0; i < size; i++) {
WakeLock stats = mWakeLocks.get(i);
stats.writeToParcel(out);
}
}
@NonNull
public static final Creator<WakeLockStats> CREATOR =
new Creator<WakeLockStats>() {
public WakeLockStats createFromParcel(Parcel in) {
return new WakeLockStats(in);
}
public WakeLockStats[] newArray(int size) {
return new WakeLockStats[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public String toString() {
return "WakeLockStats " + mWakeLocks;
}
}

View File

@@ -22,6 +22,7 @@ import android.bluetooth.BluetoothActivityEnergyInfo;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.ParcelFileDescriptor;
import android.os.WakeLockStats;
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
import android.os.connectivity.WifiActivityEnergyInfo;
@@ -157,6 +158,10 @@ interface IBatteryStats {
/** {@hide} */
GpsBatteryStats getGpsBatteryStats();
/** {@hide} */
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BATTERY_STATS)")
WakeLockStats getWakeLockStats();
HealthStatsParceler takeUidSnapshot(int uid);
HealthStatsParceler[] takeUidSnapshots(in int[] uid);

View File

@@ -60,6 +60,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WakeLockStats;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.os.connectivity.CellularBatteryStats;
@@ -1143,6 +1144,37 @@ public class BatteryStatsImpl extends BatteryStats {
return mKernelWakelockStats;
}
@Override
public WakeLockStats getWakeLockStats() {
final long realtimeMs = mClock.elapsedRealtime();
final long realtimeUs = realtimeMs * 1000;
List<WakeLockStats.WakeLock> uidWakeLockStats = new ArrayList<>();
for (int i = mUidStats.size() - 1; i >= 0; i--) {
final Uid uid = mUidStats.valueAt(i);
final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
uid.mWakelockStats.getMap();
for (int j = wakelockStats.size() - 1; j >= 0; j--) {
final String name = wakelockStats.keyAt(j);
final Uid.Wakelock wakelock = (Uid.Wakelock) wakelockStats.valueAt(j);
final DualTimer timer = wakelock.mTimerPartial;
if (timer != null) {
final long totalTimeLockHeldMs =
timer.getTotalTimeLocked(realtimeUs, STATS_SINCE_CHARGED) / 1000;
if (totalTimeLockHeldMs != 0) {
uidWakeLockStats.add(
new WakeLockStats.WakeLock(uid.getUid(), name,
timer.getCountLocked(STATS_SINCE_CHARGED),
totalTimeLockHeldMs,
timer.isRunningLocked()
? timer.getCurrentDurationMsLocked(realtimeMs)
: 0));
}
}
}
}
return new WakeLockStats(uidWakeLockStats);
}
String mLastWakeupReason = null;
long mLastWakeupUptimeMs = 0;
private final HashMap<String, SamplingTimer> mWakeupReasonStats = new HashMap<>();

View File

@@ -22,6 +22,8 @@ import static android.os.BatteryStats.Uid.PROCESS_STATE_CACHED;
import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -36,6 +38,7 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.os.BatteryStats;
import android.os.WakeLockStats;
import android.util.SparseArray;
import android.view.Display;
@@ -50,6 +53,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
@LargeTest
@RunWith(AndroidJUnit4.class)
@SuppressWarnings("GuardedBy")
@@ -507,4 +512,51 @@ public class BatteryStatsImplTest {
final BatteryStatsImpl.Uid u = mBatteryStatsImpl.getUidStatsLocked(parentUid);
u.addIsolatedUid(childUid);
}
@Test
public void testGetWakeLockStats() {
mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
// First wakelock, acquired once, not currently held
mMockClock.realtime = 1000;
mBatteryStatsImpl.noteStartWakeLocked(10100, 100, null, "wakeLock1", null,
BatteryStats.WAKE_TYPE_PARTIAL, false);
mMockClock.realtime = 3000;
mBatteryStatsImpl.noteStopWakeLocked(10100, 100, null, "wakeLock1", null,
BatteryStats.WAKE_TYPE_PARTIAL);
// Second wakelock, acquired twice, still held
mMockClock.realtime = 4000;
mBatteryStatsImpl.noteStartWakeLocked(10200, 101, null, "wakeLock2", null,
BatteryStats.WAKE_TYPE_PARTIAL, false);
mMockClock.realtime = 5000;
mBatteryStatsImpl.noteStopWakeLocked(10200, 101, null, "wakeLock2", null,
BatteryStats.WAKE_TYPE_PARTIAL);
mMockClock.realtime = 6000;
mBatteryStatsImpl.noteStartWakeLocked(10200, 101, null, "wakeLock2", null,
BatteryStats.WAKE_TYPE_PARTIAL, false);
mMockClock.realtime = 9000;
List<WakeLockStats.WakeLock> wakeLockStats =
mBatteryStatsImpl.getWakeLockStats().getWakeLocks();
assertThat(wakeLockStats).hasSize(2);
WakeLockStats.WakeLock wakeLock1 = wakeLockStats.stream()
.filter(wl -> wl.uid == 10100 && wl.name.equals("wakeLock1")).findFirst().get();
assertThat(wakeLock1.timesAcquired).isEqualTo(1);
assertThat(wakeLock1.timeHeldMs).isEqualTo(0); // Not currently held
assertThat(wakeLock1.totalTimeHeldMs).isEqualTo(2000); // 3000-1000
WakeLockStats.WakeLock wakeLock2 = wakeLockStats.stream()
.filter(wl -> wl.uid == 10200 && wl.name.equals("wakeLock2")).findFirst().get();
assertThat(wakeLock2.timesAcquired).isEqualTo(2);
assertThat(wakeLock2.timeHeldMs).isEqualTo(3000); // 9000-6000
assertThat(wakeLock2.totalTimeHeldMs).isEqualTo(4000); // (5000-4000) + (9000-6000)
}
}

View File

@@ -56,6 +56,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WakeLockStats;
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
import android.os.connectivity.GpsBatteryStats;
@@ -2595,6 +2596,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
}
/**
* Gets a snapshot of wake lock stats
* @hide
*/
public WakeLockStats getWakeLockStats() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BATTERY_STATS, null);
// Wait for the completion of pending works if there is any
awaitCompletion();
synchronized (mStats) {
return mStats.getWakeLockStats();
}
}
/**
* Gets a snapshot of the system health for a particular uid.
*/