Merge "Include binder stats in battery stats" am: 83d1954afe
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1708610 Change-Id: I17856dbb12a1c1cff7b4d6df740a016876b0bb1a
This commit is contained in:
@@ -40,6 +40,7 @@ import android.net.Uri;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.BatteryStats;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBatteryPropertiesRegistrar;
|
||||
@@ -71,6 +72,7 @@ import android.telephony.SignalStrength;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.AtomicFile;
|
||||
import android.util.IntArray;
|
||||
import android.util.KeyValueListParser;
|
||||
@@ -122,6 +124,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -6130,10 +6133,11 @@ public class BatteryStatsImpl extends BatteryStats {
|
||||
* Records timing data related to an incoming Binder call in order to attribute
|
||||
* the power consumption to the calling app.
|
||||
*/
|
||||
public void noteBinderCallStats(int workSourceUid,
|
||||
public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
|
||||
Collection<BinderCallsStats.CallStat> callStats) {
|
||||
synchronized (this) {
|
||||
getUidStatsLocked(workSourceUid).noteBinderCallStatsLocked(callStats);
|
||||
getUidStatsLocked(workSourceUid).noteBinderCallStatsLocked(incrementalCallCount,
|
||||
callStats);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6579,6 +6583,65 @@ public class BatteryStatsImpl extends BatteryStats {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accumulates stats for a specific binder transaction.
|
||||
*/
|
||||
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||
protected static class BinderCallStats {
|
||||
static final Comparator<BinderCallStats> COMPARATOR =
|
||||
Comparator.comparing(BinderCallStats::getClassName)
|
||||
.thenComparing(BinderCallStats::getMethodName);
|
||||
|
||||
public Class<? extends Binder> binderClass;
|
||||
public int transactionCode;
|
||||
public String methodName;
|
||||
|
||||
public long callCount;
|
||||
public long recordedCallCount;
|
||||
public long recordedCpuTimeMicros;
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return binderClass.hashCode() * 31 + transactionCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof BinderCallStats)) {
|
||||
return false;
|
||||
}
|
||||
BinderCallStats bcsk = (BinderCallStats) obj;
|
||||
return binderClass.equals(bcsk.binderClass) && transactionCode == bcsk.transactionCode;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return binderClass.getName();
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||
public void ensureMethodName(BinderTransactionNameResolver resolver) {
|
||||
if (methodName == null) {
|
||||
methodName = resolver.getMethodName(binderClass, transactionCode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BinderCallStats{"
|
||||
+ binderClass
|
||||
+ " transaction=" + transactionCode
|
||||
+ " callCount=" + callCount
|
||||
+ " recordedCallCount=" + recordedCallCount
|
||||
+ " recorderCpuTimeMicros=" + recordedCpuTimeMicros
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The statistics associated with a particular uid.
|
||||
*/
|
||||
@@ -6752,6 +6815,16 @@ public class BatteryStatsImpl extends BatteryStats {
|
||||
*/
|
||||
final SparseArray<Pid> mPids = new SparseArray<>();
|
||||
|
||||
/**
|
||||
* Grand total of system server binder calls made by this uid.
|
||||
*/
|
||||
private long mBinderCallCount;
|
||||
|
||||
/**
|
||||
* Detailed information about system server binder calls made by this uid.
|
||||
*/
|
||||
private final ArraySet<BinderCallStats> mBinderCallStats = new ArraySet<>();
|
||||
|
||||
public Uid(BatteryStatsImpl bsi, int uid) {
|
||||
mBsi = bsi;
|
||||
mUid = uid;
|
||||
@@ -6860,6 +6933,14 @@ public class BatteryStatsImpl extends BatteryStats {
|
||||
return nullIfAllZeros(mProcStateScreenOffTimeMs[procState], which);
|
||||
}
|
||||
|
||||
public long getBinderCallCount() {
|
||||
return mBinderCallCount;
|
||||
}
|
||||
|
||||
public ArraySet<BinderCallStats> getBinderCallStats() {
|
||||
return mBinderCallStats;
|
||||
}
|
||||
|
||||
public void addIsolatedUid(int isolatedUid) {
|
||||
if (mChildUids == null) {
|
||||
mChildUids = new IntArray();
|
||||
@@ -7948,6 +8029,9 @@ public class BatteryStatsImpl extends BatteryStats {
|
||||
}
|
||||
mPackageStats.clear();
|
||||
|
||||
mBinderCallCount = 0;
|
||||
mBinderCallStats.clear();
|
||||
|
||||
mLastStepUserTime = mLastStepSystemTime = 0;
|
||||
mCurStepUserTime = mCurStepSystemTime = 0;
|
||||
|
||||
@@ -8703,15 +8787,38 @@ public class BatteryStatsImpl extends BatteryStats {
|
||||
}
|
||||
}
|
||||
|
||||
// Reusable object used as a key to lookup values in mBinderCallStats
|
||||
private static BinderCallStats sTempBinderCallStats = new BinderCallStats();
|
||||
|
||||
/**
|
||||
* Notes incoming binder call stats associated with this work source UID.
|
||||
*/
|
||||
public void noteBinderCallStatsLocked(Collection<BinderCallsStats.CallStat> callStats) {
|
||||
public void noteBinderCallStatsLocked(long incrementalCallCount,
|
||||
Collection<BinderCallsStats.CallStat> callStats) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "noteBinderCalls() workSourceUid = [" + mUid + "], callStats = ["
|
||||
Slog.d(TAG, "noteBinderCalls() workSourceUid = [" + mUid + "], "
|
||||
+ " incrementalCallCount: " + incrementalCallCount + " callStats = ["
|
||||
+ new ArrayList<>(callStats) + "]");
|
||||
}
|
||||
// TODO(dplotnikov): finish the implementation by actually remembering the stats
|
||||
mBinderCallCount += incrementalCallCount;
|
||||
for (BinderCallsStats.CallStat stat : callStats) {
|
||||
BinderCallStats bcs;
|
||||
sTempBinderCallStats.binderClass = stat.binderClass;
|
||||
sTempBinderCallStats.transactionCode = stat.transactionCode;
|
||||
int index = mBinderCallStats.indexOf(sTempBinderCallStats);
|
||||
if (index >= 0) {
|
||||
bcs = mBinderCallStats.valueAt(index);
|
||||
} else {
|
||||
bcs = new BinderCallStats();
|
||||
bcs.binderClass = stat.binderClass;
|
||||
bcs.transactionCode = stat.transactionCode;
|
||||
mBinderCallStats.add(bcs);
|
||||
}
|
||||
|
||||
bcs.callCount += stat.incrementalCallCount;
|
||||
bcs.recordedCallCount = stat.recordedCallCount;
|
||||
bcs.recordedCpuTimeMicros = stat.cpuTimeMicros;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -13235,6 +13342,45 @@ public class BatteryStatsImpl extends BatteryStats {
|
||||
pw.print(uid.getUserCpuTimeUs(STATS_SINCE_CHARGED) / 1000); pw.print(" ");
|
||||
pw.println(uid.getSystemCpuTimeUs(STATS_SINCE_CHARGED) / 1000);
|
||||
}
|
||||
pw.println("Per UID system service calls:");
|
||||
BinderTransactionNameResolver nameResolver = new BinderTransactionNameResolver();
|
||||
for (int i = 0; i < size; i++) {
|
||||
int u = mUidStats.keyAt(i);
|
||||
Uid uid = mUidStats.get(u);
|
||||
long binderCallCount = uid.getBinderCallCount();
|
||||
if (binderCallCount != 0) {
|
||||
pw.print(" ");
|
||||
pw.print(u);
|
||||
pw.print(" system service calls: ");
|
||||
pw.print(binderCallCount);
|
||||
ArraySet<BinderCallStats> binderCallStats = uid.getBinderCallStats();
|
||||
if (!binderCallStats.isEmpty()) {
|
||||
pw.println(", including");
|
||||
BinderCallStats[] bcss = new BinderCallStats[binderCallStats.size()];
|
||||
binderCallStats.toArray(bcss);
|
||||
for (BinderCallStats bcs : bcss) {
|
||||
bcs.ensureMethodName(nameResolver);
|
||||
}
|
||||
Arrays.sort(bcss, BinderCallStats.COMPARATOR);
|
||||
for (BinderCallStats callStats : bcss) {
|
||||
pw.print(" ");
|
||||
pw.print(callStats.getClassName());
|
||||
pw.print('#');
|
||||
pw.print(callStats.getMethodName());
|
||||
pw.print(" calls: ");
|
||||
pw.print(callStats.callCount);
|
||||
if (callStats.recordedCallCount != 0) {
|
||||
pw.print(" time: ");
|
||||
pw.print(callStats.callCount * callStats.recordedCpuTimeMicros
|
||||
/ callStats.recordedCallCount / 1000);
|
||||
}
|
||||
pw.println();
|
||||
}
|
||||
} else {
|
||||
pw.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
pw.println("Per UID CPU active time in ms:");
|
||||
for (int i = 0; i < size; i++) {
|
||||
int u = mUidStats.keyAt(i);
|
||||
|
||||
@@ -36,13 +36,10 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.os.BinderInternal.CallSession;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
@@ -118,8 +115,13 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
||||
for (int i = 0; i < size; i++) {
|
||||
UidEntry uidEntry = mUidEntries.get(mSendUidsToObserver.valueAt(i));
|
||||
if (uidEntry != null) {
|
||||
ArrayMap<CallStatKey, CallStat> callStats = uidEntry.mCallStats;
|
||||
mCallStatsObserver.noteCallStats(uidEntry.workSourceUid,
|
||||
uidEntry.getCallStatsList());
|
||||
uidEntry.incrementalCallCount, callStats.values());
|
||||
uidEntry.incrementalCallCount = 0;
|
||||
for (int j = callStats.size() - 1; j >= 0; j--) {
|
||||
callStats.valueAt(j).incrementalCallCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
mSendUidsToObserver.clear();
|
||||
@@ -248,6 +250,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
||||
|
||||
final UidEntry uidEntry = getUidEntry(workSourceUid);
|
||||
uidEntry.callCount++;
|
||||
uidEntry.incrementalCallCount++;
|
||||
|
||||
if (recordCall) {
|
||||
uidEntry.cpuTimeMicros += duration;
|
||||
@@ -263,6 +266,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
||||
}
|
||||
|
||||
callStat.callCount++;
|
||||
callStat.incrementalCallCount++;
|
||||
callStat.recordedCallCount++;
|
||||
callStat.cpuTimeMicros += duration;
|
||||
callStat.maxCpuTimeMicros = Math.max(callStat.maxCpuTimeMicros, duration);
|
||||
@@ -284,6 +288,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
||||
screenInteractive);
|
||||
if (callStat != null) {
|
||||
callStat.callCount++;
|
||||
callStat.incrementalCallCount++;
|
||||
}
|
||||
}
|
||||
if (mCallStatsObserver != null && !UserHandle.isCore(workSourceUid)) {
|
||||
@@ -322,30 +327,6 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Method getDefaultTransactionNameMethod(Class<? extends Binder> binder) {
|
||||
try {
|
||||
return binder.getMethod("getDefaultTransactionName", int.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// The method might not be present for stubs not generated with AIDL.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String resolveTransactionCode(Method getDefaultTransactionName,
|
||||
int transactionCode) {
|
||||
String resolvedCode = null;
|
||||
if (getDefaultTransactionName != null) {
|
||||
try {
|
||||
resolvedCode = (String) getDefaultTransactionName.invoke(null, transactionCode);
|
||||
} catch (IllegalAccessException | InvocationTargetException | ClassCastException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return resolvedCode == null ? String.valueOf(transactionCode) : resolvedCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is expensive to call.
|
||||
*/
|
||||
@@ -384,28 +365,23 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
||||
|
||||
// Resolve codes outside of the lock since it can be slow.
|
||||
ExportedCallStat previous = null;
|
||||
// Cache the previous method/transaction code.
|
||||
Method getDefaultTransactionName = null;
|
||||
String previousMethodName = null;
|
||||
resultCallStats.sort(BinderCallsStats::compareByBinderClassAndCode);
|
||||
BinderTransactionNameResolver resolver = new BinderTransactionNameResolver();
|
||||
for (ExportedCallStat exported : resultCallStats) {
|
||||
final boolean isClassDifferent = previous == null
|
||||
|| !previous.className.equals(exported.className);
|
||||
if (isClassDifferent) {
|
||||
getDefaultTransactionName = getDefaultTransactionNameMethod(exported.binderClass);
|
||||
}
|
||||
|
||||
final boolean isCodeDifferent = previous == null
|
||||
|| previous.transactionCode != exported.transactionCode;
|
||||
final String methodName;
|
||||
if (isClassDifferent || isCodeDifferent) {
|
||||
methodName = resolveTransactionCode(
|
||||
getDefaultTransactionName, exported.transactionCode);
|
||||
methodName = resolver.getMethodName(exported.binderClass, exported.transactionCode);
|
||||
} else {
|
||||
methodName = previousMethodName;
|
||||
}
|
||||
previousMethodName = methodName;
|
||||
exported.methodName = methodName;
|
||||
previous = exported;
|
||||
}
|
||||
|
||||
// Debug entries added to help validate the data.
|
||||
@@ -703,8 +679,10 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
||||
public long maxRequestSizeBytes;
|
||||
public long maxReplySizeBytes;
|
||||
public long exceptionCount;
|
||||
// Call count since reset
|
||||
public long incrementalCallCount;
|
||||
|
||||
CallStat(int callingUid, Class<? extends Binder> binderClass, int transactionCode,
|
||||
public CallStat(int callingUid, Class<? extends Binder> binderClass, int transactionCode,
|
||||
boolean screenInteractive) {
|
||||
this.callingUid = callingUid;
|
||||
this.binderClass = binderClass;
|
||||
@@ -714,12 +692,14 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// This is expensive, but CallStat.toString() is only used for debugging.
|
||||
String methodName = new BinderTransactionNameResolver().getMethodName(binderClass,
|
||||
transactionCode);
|
||||
return "CallStat{"
|
||||
+ "callingUid=" + callingUid
|
||||
+ ", transaction=" + binderClass.getSimpleName()
|
||||
+ '.' + resolveTransactionCode(
|
||||
getDefaultTransactionNameMethod(binderClass), transactionCode)
|
||||
+ ", transaction=" + binderClass.getSimpleName() + '.' + methodName
|
||||
+ ", callCount=" + callCount
|
||||
+ ", incrementalCallCount=" + incrementalCallCount
|
||||
+ ", recordedCallCount=" + recordedCallCount
|
||||
+ ", cpuTimeMicros=" + cpuTimeMicros
|
||||
+ ", latencyMicros=" + latencyMicros
|
||||
@@ -772,13 +752,15 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
||||
// Approximate total CPU usage can be computed by
|
||||
// cpuTimeMicros * callCount / recordedCallCount
|
||||
public long cpuTimeMicros;
|
||||
// Call count that gets reset after delivery to BatteryStats
|
||||
public long incrementalCallCount;
|
||||
|
||||
UidEntry(int uid) {
|
||||
this.workSourceUid = uid;
|
||||
}
|
||||
|
||||
// Aggregate time spent per each call name: call_desc -> cpu_time_micros
|
||||
private Map<CallStatKey, CallStat> mCallStats = new ArrayMap<>();
|
||||
private ArrayMap<CallStatKey, CallStat> mCallStats = new ArrayMap<>();
|
||||
private CallStatKey mTempKey = new CallStatKey();
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -141,7 +141,8 @@ public class BinderInternal {
|
||||
/**
|
||||
* Notes incoming binder call stats associated with this work source UID.
|
||||
*/
|
||||
void noteCallStats(int workSourceUid, Collection<BinderCallsStats.CallStat> callStats);
|
||||
void noteCallStats(int workSourceUid, long incrementalCallCount,
|
||||
Collection<BinderCallsStats.CallStat> callStats);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 com.android.internal.os;
|
||||
|
||||
import android.os.Binder;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Maps a binder class and transaction code to the default transaction name. Since this
|
||||
* resolution is class-based as opposed to instance-based, any custom implementation of
|
||||
* {@link Binder#getTransactionName} will be ignored.
|
||||
*
|
||||
* The class is NOT thread safe
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||
public class BinderTransactionNameResolver {
|
||||
private static final Method NO_GET_DEFAULT_TRANSACTION_NAME_METHOD;
|
||||
|
||||
/**
|
||||
* Generates the default transaction method name, which is just the transaction code.
|
||||
* Used when the binder does not define a static "getDefaultTransactionName" method.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static String noDefaultTransactionName(int transactionCode) {
|
||||
return String.valueOf(transactionCode);
|
||||
}
|
||||
|
||||
static {
|
||||
try {
|
||||
NO_GET_DEFAULT_TRANSACTION_NAME_METHOD = BinderTransactionNameResolver.class.getMethod(
|
||||
"noDefaultTransactionName", int.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final HashMap<Class<? extends Binder>, Method>
|
||||
mGetDefaultTransactionNameMethods = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Given a binder class name and transaction code, returns the corresponding method name.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public String getMethodName(Class<? extends Binder> binderClass, int transactionCode) {
|
||||
Method method = mGetDefaultTransactionNameMethods.get(binderClass);
|
||||
if (method == null) {
|
||||
try {
|
||||
method = binderClass.getMethod("getDefaultTransactionName", int.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
method = NO_GET_DEFAULT_TRANSACTION_NAME_METHOD;
|
||||
}
|
||||
if (method.getReturnType() != String.class
|
||||
|| !Modifier.isStatic(method.getModifiers())) {
|
||||
method = NO_GET_DEFAULT_TRANSACTION_NAME_METHOD;
|
||||
}
|
||||
mGetDefaultTransactionNameMethods.put(binderClass, method);
|
||||
}
|
||||
|
||||
try {
|
||||
return (String) method.invoke(null, transactionCode);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 com.android.internal.os;
|
||||
|
||||
import android.os.Binder;
|
||||
import android.os.Process;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Test cases for android.os.BatteryStats, system server Binder call stats.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class BatteryStatsBinderCallStatsTest extends TestCase {
|
||||
|
||||
private static final int TRANSACTION_CODE = 100;
|
||||
|
||||
/**
|
||||
* Test BatteryStatsImpl.Uid.noteBinderCallStats.
|
||||
*/
|
||||
@Test
|
||||
public void testNoteBinderCallStats() throws Exception {
|
||||
final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
|
||||
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
|
||||
|
||||
int callingUid = Process.FIRST_APPLICATION_UID + 1;
|
||||
int workSourceUid = Process.FIRST_APPLICATION_UID + 1;
|
||||
|
||||
Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
|
||||
BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(callingUid,
|
||||
MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */);
|
||||
stat1.incrementalCallCount = 21;
|
||||
stat1.recordedCallCount = 5;
|
||||
stat1.cpuTimeMicros = 1000;
|
||||
callStats.add(stat1);
|
||||
|
||||
bi.noteBinderCallStats(workSourceUid, 42, callStats);
|
||||
|
||||
callStats.clear();
|
||||
BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid,
|
||||
MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */);
|
||||
stat2.incrementalCallCount = 9;
|
||||
stat2.recordedCallCount = 8;
|
||||
stat2.cpuTimeMicros = 500;
|
||||
callStats.add(stat2);
|
||||
|
||||
bi.noteBinderCallStats(workSourceUid, 8, callStats);
|
||||
|
||||
BatteryStatsImpl.Uid uid = bi.getUidStatsLocked(workSourceUid);
|
||||
assertEquals(42 + 8, uid.getBinderCallCount());
|
||||
|
||||
BinderTransactionNameResolver resolver = new BinderTransactionNameResolver();
|
||||
ArraySet<BatteryStatsImpl.BinderCallStats> stats = uid.getBinderCallStats();
|
||||
assertEquals(1, stats.size());
|
||||
BatteryStatsImpl.BinderCallStats value = stats.valueAt(0);
|
||||
value.ensureMethodName(resolver);
|
||||
assertEquals("testMethod", value.getMethodName());
|
||||
assertEquals(21 + 9, value.callCount);
|
||||
assertEquals(8, value.recordedCallCount);
|
||||
assertEquals(500, value.recordedCpuTimeMicros);
|
||||
}
|
||||
|
||||
private static class MockBinder extends Binder {
|
||||
public static String getDefaultTransactionName(int txCode) {
|
||||
return txCode == TRANSACTION_CODE ? "testMethod" : "unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import org.junit.runners.Suite;
|
||||
@Suite.SuiteClasses({
|
||||
BatteryStatsCpuTimesTest.class,
|
||||
BatteryStatsBackgroundStatsTest.class,
|
||||
BatteryStatsBinderCallStatsTest.class,
|
||||
BatteryStatsCounterTest.class,
|
||||
BatteryStatsDualTimerTest.class,
|
||||
BatteryStatsDurationTimerTest.class,
|
||||
|
||||
@@ -767,7 +767,9 @@ public class BinderCallsStatsTest {
|
||||
bcs.setTrackScreenInteractive(false);
|
||||
|
||||
final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>();
|
||||
bcs.setCallStatsObserver((workSourceUid, callStats) -> callStatsList.addAll(callStats));
|
||||
bcs.setCallStatsObserver(
|
||||
(workSourceUid, incrementalCallCount, callStats) -> callStatsList.addAll(
|
||||
callStats));
|
||||
|
||||
Binder binder = new Binder();
|
||||
|
||||
|
||||
@@ -49,6 +49,6 @@ public abstract class BatteryStatsInternal {
|
||||
/**
|
||||
* Informs battery stats of binder stats for the given work source UID.
|
||||
*/
|
||||
public abstract void noteBinderCallStats(int workSourceUid,
|
||||
public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount,
|
||||
Collection<BinderCallsStats.CallStat> callStats);
|
||||
}
|
||||
|
||||
@@ -308,9 +308,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noteBinderCallStats(int workSourceUid,
|
||||
public void noteBinderCallStats(int workSourceUid, long incrementatCallCount,
|
||||
Collection<BinderCallsStats.CallStat> callStats) {
|
||||
mStats.noteBinderCallStats(workSourceUid, callStats);
|
||||
mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user