diff options
4 files changed, 592 insertions, 1 deletions
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index b5fd116e68aa..3eea72d5819b 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -203,6 +203,8 @@ public abstract class BatteryStats implements Parcelable { private static final String APK_DATA = "apk"; private static final String PROCESS_DATA = "pr"; private static final String CPU_DATA = "cpu"; + private static final String GLOBAL_CPU_FREQ_DATA = "gcf"; + private static final String CPU_TIMES_AT_FREQ_DATA = "ctf"; private static final String SENSOR_DATA = "sr"; private static final String VIBRATOR_DATA = "vib"; private static final String FOREGROUND_DATA = "fg"; @@ -265,6 +267,13 @@ public abstract class BatteryStats implements Parcelable { private final Formatter mFormatter = new Formatter(mFormatBuilder); /** + * Indicates times spent by the uid at each cpu frequency in all process states. + * + * Other types might include times spent in foreground, background etc. + */ + private final String UID_TIMES_TYPE_ALL = "A"; + + /** * State for keeping track of counting information. */ public static abstract class Counter { @@ -303,6 +312,24 @@ public abstract class BatteryStats implements Parcelable { } /** + * State for keeping track of array of long counting information. + */ + public static abstract class LongCounterArray { + /** + * Returns the counts associated with this Counter for the + * selected type of statistics. + * + * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT + */ + public abstract long[] getCountsLocked(int which); + + /** + * Temporary for debugging. + */ + public abstract void logState(Printer pw, String prefix); + } + + /** * Container class that aggregates counters for transmit, receive, and idle state of a * radio controller. */ @@ -523,6 +550,9 @@ public abstract class BatteryStats implements Parcelable { public abstract Timer getBluetoothScanBackgroundTimer(); public abstract Counter getBluetoothScanResultCounter(); + public abstract long[] getCpuFreqTimes(int which); + public abstract long[] getScreenOffCpuFreqTimes(int which); + // Note: the following times are disjoint. They can be added together to find the // total time a uid has had any processes running at all. @@ -1077,6 +1107,8 @@ public abstract class BatteryStats implements Parcelable { public abstract long getNextMaxDailyDeadline(); + public abstract long[] getCpuFreqs(); + public final static class HistoryTag { public String string; public int uid; @@ -3274,6 +3306,15 @@ public abstract class BatteryStats implements Parcelable { } } + final long[] cpuFreqs = getCpuFreqs(); + if (cpuFreqs != null) { + sb.setLength(0); + for (int i = 0; i < cpuFreqs.length; ++i) { + sb.append((i == 0 ? "" : ",") + cpuFreqs[i]); + } + dumpLine(pw, 0 /* uid */, category, GLOBAL_CPU_FREQ_DATA, sb.toString()); + } + for (int iu = 0; iu < NU; iu++) { final int uid = uidStats.keyAt(iu); if (reqUid >= 0 && uid != reqUid) { @@ -3506,6 +3547,27 @@ public abstract class BatteryStats implements Parcelable { 0 /* old cpu power, keep for compatibility */); } + final long[] cpuFreqTimeMs = u.getCpuFreqTimes(which); + // If total cpuFreqTimes is null, then we don't need to check for screenOffCpuFreqTimes. + if (cpuFreqTimeMs != null) { + sb.setLength(0); + for (int i = 0; i < cpuFreqTimeMs.length; ++i) { + sb.append((i == 0 ? "" : ",") + cpuFreqTimeMs[i]); + } + final long[] screenOffCpuFreqTimeMs = u.getScreenOffCpuFreqTimes(which); + if (screenOffCpuFreqTimeMs != null) { + for (int i = 0; i < screenOffCpuFreqTimeMs.length; ++i) { + sb.append("," + screenOffCpuFreqTimeMs[i]); + } + } else { + for (int i = 0; i < cpuFreqTimeMs.length; ++i) { + sb.append(",0"); + } + } + dumpLine(pw, uid, category, CPU_TIMES_AT_FREQ_DATA, UID_TIMES_TYPE_ALL, + cpuFreqTimeMs.length, sb.toString()); + } + final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); for (int ipr=processStats.size()-1; ipr>=0; ipr--) { @@ -4373,6 +4435,16 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); } + final long[] cpuFreqs = getCpuFreqs(); + if (cpuFreqs != null) { + sb.setLength(0); + sb.append("CPU freqs:"); + for (int i = 0; i < cpuFreqs.length; ++i) { + sb.append(" " + cpuFreqs[i]); + } + pw.println(sb.toString()); + } + for (int iu=0; iu<NU; iu++) { final int uid = uidStats.keyAt(iu); if (reqUid >= 0 && uid != reqUid && uid != Process.SYSTEM_UID) { @@ -4835,6 +4907,25 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); } + final long[] cpuFreqTimes = u.getCpuFreqTimes(which); + if (cpuFreqTimes != null) { + sb.setLength(0); + sb.append(" Total cpu time per freq:"); + for (int i = 0; i < cpuFreqTimes.length; ++i) { + sb.append(" " + cpuFreqTimes[i]); + } + pw.println(sb.toString()); + } + final long[] screenOffCpuFreqTimes = u.getScreenOffCpuFreqTimes(which); + if (screenOffCpuFreqTimes != null) { + sb.setLength(0); + sb.append(" Total screen-off cpu time per freq:"); + for (int i = 0; i < screenOffCpuFreqTimes.length; ++i) { + sb.append(" " + screenOffCpuFreqTimes[i]); + } + pw.println(sb.toString()); + } + final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); for (int ipr=processStats.size()-1; ipr>=0; ipr--) { diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 6b185919d7f6..be4df51b53ee 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -16,6 +16,7 @@ package com.android.internal.os; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.bluetooth.BluetoothActivityEnergyInfo; @@ -87,6 +88,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; @@ -114,7 +116,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 156 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 157 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -149,6 +151,8 @@ public class BatteryStatsImpl extends BatteryStats { private final KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader(); private KernelCpuSpeedReader[] mKernelCpuSpeedReaders; + private final KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader = + new KernelUidCpuFreqTimeReader(); private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats = new KernelMemoryBandwidthStats(); @@ -570,6 +574,8 @@ public class BatteryStatsImpl extends BatteryStats { private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry(); + private long[] mCpuFreqs; + private PowerProfile mPowerProfile; /* @@ -957,6 +963,131 @@ public class BatteryStatsImpl extends BatteryStats { } } + public static class LongSamplingCounterArray extends LongCounterArray implements TimeBaseObs { + final TimeBase mTimeBase; + long[] mCounts; + long[] mLoadedCounts; + long[] mUnpluggedCounts; + long[] mPluggedCounts; + + LongSamplingCounterArray(TimeBase timeBase, Parcel in) { + mTimeBase = timeBase; + mPluggedCounts = in.createLongArray(); + mCounts = copyArray(mPluggedCounts, mCounts); + mLoadedCounts = in.createLongArray(); + mUnpluggedCounts = in.createLongArray(); + timeBase.add(this); + } + + LongSamplingCounterArray(TimeBase timeBase) { + mTimeBase = timeBase; + timeBase.add(this); + } + + public void writeToParcel(Parcel out) { + out.writeLongArray(mCounts); + out.writeLongArray(mLoadedCounts); + out.writeLongArray(mUnpluggedCounts); + } + + @Override + public void onTimeStarted(long elapsedRealTime, long baseUptime, long baseRealtime) { + mUnpluggedCounts = copyArray(mPluggedCounts, mUnpluggedCounts); + mCounts = copyArray(mPluggedCounts, mCounts); + } + + @Override + public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) { + mPluggedCounts = copyArray(mCounts, mPluggedCounts); + } + + @Override + public long[] getCountsLocked(int which) { + long[] val = copyArray(mTimeBase.isRunning() ? mCounts : mPluggedCounts, null); + if (which == STATS_SINCE_UNPLUGGED) { + subtract(val, mUnpluggedCounts); + } else if (which != STATS_SINCE_CHARGED) { + subtract(val, mLoadedCounts); + } + return val; + } + + @Override + public void logState(Printer pw, String prefix) { + pw.println(prefix + "mCounts=" + Arrays.toString(mCounts) + + " mLoadedCounts=" + Arrays.toString(mLoadedCounts) + + " mUnpluggedCounts=" + Arrays.toString(mUnpluggedCounts) + + " mPluggedCounts=" + Arrays.toString(mPluggedCounts)); + } + + void addCountLocked(long[] counts) { + if (counts == null) { + return; + } + if (mCounts == null) { + mCounts = new long[counts.length]; + } + for (int i = 0; i < counts.length; ++i) { + mCounts[i] += counts[i]; + } + } + + /** + * Clear state of this counter. + */ + void reset(boolean detachIfReset) { + fillArray(mCounts, 0); + fillArray(mLoadedCounts, 0); + fillArray(mPluggedCounts, 0); + fillArray(mUnpluggedCounts, 0); + if (detachIfReset) { + detach(); + } + } + + void detach() { + mTimeBase.remove(this); + } + + void writeSummaryFromParcelLocked(Parcel out) { + out.writeLongArray(mCounts); + } + + void readSummaryFromParcelLocked(Parcel in) { + mCounts = in.createLongArray(); + mLoadedCounts = copyArray(mCounts, mLoadedCounts); + mUnpluggedCounts = copyArray(mCounts, mUnpluggedCounts); + mPluggedCounts = copyArray(mCounts, mPluggedCounts); + } + + private void fillArray(long[] a, long val) { + if (a != null) { + Arrays.fill(a, val); + } + } + + private void subtract(@NonNull long[] val, long[] toSubtract) { + if (toSubtract == null) { + return; + } + for (int i = 0; i < val.length; i++) { + val[i] -= toSubtract[i]; + } + } + + private long[] copyArray(long[] src, long[] dest) { + if (src == null) { + return null; + } else { + if (dest == null) { + dest = new long[src.length]; + } + System.arraycopy(src, 0, dest, 0, src.length); + return dest; + } + } + } + public static class LongSamplingCounter extends LongCounter implements TimeBaseObs { final TimeBase mTimeBase; long mCount; @@ -5483,6 +5614,9 @@ public class BatteryStatsImpl extends BatteryStats { LongSamplingCounter mSystemCpuTime; LongSamplingCounter[][] mCpuClusterSpeed; + LongSamplingCounterArray mCpuFreqTimeMs; + LongSamplingCounterArray mScreenOffCpuFreqTimeMs; + /** * The statistics we have collected for this uid's wake locks. */ @@ -5560,6 +5694,42 @@ public class BatteryStatsImpl extends BatteryStats { } @Override + public long[] getCpuFreqTimes(int which) { + if (mCpuFreqTimeMs == null) { + return null; + } + final long[] cpuFreqTimes = mCpuFreqTimeMs.getCountsLocked(which); + if (cpuFreqTimes == null) { + return null; + } + // Return cpuFreqTimes only if atleast one of the elements in non-zero. + for (int i = 0; i < cpuFreqTimes.length; ++i) { + if (cpuFreqTimes[i] != 0) { + return cpuFreqTimes; + } + } + return null; + } + + @Override + public long[] getScreenOffCpuFreqTimes(int which) { + if (mScreenOffCpuFreqTimeMs == null) { + return null; + } + final long[] cpuFreqTimes = mScreenOffCpuFreqTimeMs.getCountsLocked(which); + if (cpuFreqTimes == null) { + return null; + } + // Return cpuFreqTimes only if atleast one of the elements in non-zero. + for (int i = 0; i < cpuFreqTimes.length; ++i) { + if (cpuFreqTimes[i] != 0) { + return cpuFreqTimes; + } + } + return null; + } + + @Override public ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> getWakelockStats() { return mWakelockStats.getMap(); } @@ -6354,6 +6524,13 @@ public class BatteryStatsImpl extends BatteryStats { } } + if (mCpuFreqTimeMs != null) { + mCpuFreqTimeMs.reset(false); + } + if (mScreenOffCpuFreqTimeMs != null) { + mScreenOffCpuFreqTimeMs.reset(false); + } + resetLongCounterIfNotNull(mMobileRadioApWakeupCount, false); resetLongCounterIfNotNull(mWifiRadioApWakeupCount, false); @@ -6523,6 +6700,13 @@ public class BatteryStatsImpl extends BatteryStats { } } + if (mCpuFreqTimeMs != null) { + mCpuFreqTimeMs.detach(); + } + if (mScreenOffCpuFreqTimeMs != null) { + mScreenOffCpuFreqTimeMs.detach(); + } + detachLongCounterIfNotNull(mMobileRadioApWakeupCount); detachLongCounterIfNotNull(mWifiRadioApWakeupCount); } @@ -6739,6 +6923,19 @@ public class BatteryStatsImpl extends BatteryStats { out.writeInt(0); } + if (mCpuFreqTimeMs != null) { + out.writeInt(1); + mCpuFreqTimeMs.writeToParcel(out); + } else { + out.writeInt(0); + } + if (mScreenOffCpuFreqTimeMs != null) { + out.writeInt(1); + mScreenOffCpuFreqTimeMs.writeToParcel(out); + } else { + out.writeInt(0); + } + if (mMobileRadioApWakeupCount != null) { out.writeInt(1); mMobileRadioApWakeupCount.writeToParcel(out); @@ -6987,6 +7184,18 @@ public class BatteryStatsImpl extends BatteryStats { } if (in.readInt() != 0) { + mCpuFreqTimeMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase, in); + } else { + mCpuFreqTimeMs = null; + } + if (in.readInt() != 0) { + mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray( + mBsi.mOnBatteryScreenOffTimeBase, in); + } else { + mScreenOffCpuFreqTimeMs = null; + } + + if (in.readInt() != 0) { mMobileRadioApWakeupCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in); } else { mMobileRadioApWakeupCount = null; @@ -8192,6 +8401,10 @@ public class BatteryStatsImpl extends BatteryStats { } } + public long[] getCpuFreqs() { + return mCpuFreqs; + } + public BatteryStatsImpl(File systemDir, Handler handler, ExternalStatsSync externalSync) { this(new SystemClocks(), systemDir, handler, externalSync, null); } @@ -9812,6 +10025,8 @@ public class BatteryStatsImpl extends BatteryStats { } }); + readKernelUidCpuFreqTimesLocked(); + final long elapse = (mClocks.elapsedRealtime() - startTimeMs); if (DEBUG_ENERGY_CPU || (elapse >= 100)) { Slog.d(TAG, "Reading cpu stats took " + elapse + " ms"); @@ -9900,6 +10115,30 @@ public class BatteryStatsImpl extends BatteryStats { } } + void readKernelUidCpuFreqTimesLocked() { + mKernelUidCpuFreqTimeReader.readDelta(!mOnBatteryInternal ? null : + new KernelUidCpuFreqTimeReader.Callback() { + @Override + public void onCpuFreqs(long[] cpuFreqs) { + mCpuFreqs = cpuFreqs; + } + + @Override + public void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs) { + final Uid u = getUidStatsLocked(uid); + if (u.mCpuFreqTimeMs == null) { + u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase); + } + u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs); + if (u.mScreenOffCpuFreqTimeMs == null) { + u.mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray( + mOnBatteryScreenOffTimeBase); + } + u.mScreenOffCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs); + } + }); + } + boolean setChargingLocked(boolean charging) { if (mCharging != charging) { mCharging = charging; @@ -10988,6 +11227,8 @@ public class BatteryStatsImpl extends BatteryStats { } } + mCpuFreqs = in.createLongArray(); + final int NU = in.readInt(); if (NU > 10000) { throw new ParcelFormatException("File corrupt: too many uids " + NU); @@ -11111,6 +11352,20 @@ public class BatteryStatsImpl extends BatteryStats { } if (in.readInt() != 0) { + u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase); + u.mCpuFreqTimeMs.readSummaryFromParcelLocked(in); + } else { + u.mCpuFreqTimeMs = null; + } + if (in.readInt() != 0) { + u.mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray( + mOnBatteryScreenOffTimeBase); + u.mScreenOffCpuFreqTimeMs.readSummaryFromParcelLocked(in); + } else { + u.mScreenOffCpuFreqTimeMs = null; + } + + if (in.readInt() != 0) { u.mMobileRadioApWakeupCount = new LongSamplingCounter(mOnBatteryTimeBase); u.mMobileRadioApWakeupCount.readSummaryFromParcelLocked(in); } else { @@ -11360,6 +11615,8 @@ public class BatteryStatsImpl extends BatteryStats { } } + out.writeLongArray(mCpuFreqs); + final int NU = mUidStats.size(); out.writeInt(NU); for (int iu = 0; iu < NU; iu++) { @@ -11504,6 +11761,19 @@ public class BatteryStatsImpl extends BatteryStats { out.writeInt(0); } + if (u.mCpuFreqTimeMs != null) { + out.writeInt(1); + u.mCpuFreqTimeMs.writeSummaryFromParcelLocked(out); + } else { + out.writeInt(0); + } + if (u.mScreenOffCpuFreqTimeMs != null) { + out.writeInt(1); + u.mScreenOffCpuFreqTimeMs.writeSummaryFromParcelLocked(out); + } else { + out.writeInt(0); + } + if (u.mMobileRadioApWakeupCount != null) { out.writeInt(1); u.mMobileRadioApWakeupCount.writeSummaryFromParcelLocked(out); @@ -11794,6 +12064,8 @@ public class BatteryStatsImpl extends BatteryStats { mFlashlightTurnedOnTimers.clear(); mCameraTurnedOnTimers.clear(); + mCpuFreqs = in.createLongArray(); + int numUids = in.readInt(); mUidStats.clear(); for (int i = 0; i < numUids; i++) { @@ -11953,6 +12225,8 @@ public class BatteryStatsImpl extends BatteryStats { } } + out.writeLongArray(mCpuFreqs); + if (inclUids) { int size = mUidStats.size(); out.writeInt(size); diff --git a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java new file mode 100644 index 000000000000..568c8830d35b --- /dev/null +++ b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2017 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.annotation.Nullable; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; + +/** + * Reads /proc/uid_time_in_state which has the format: + * + * uid: [freq1] [freq2] [freq3] ... + * [uid1]: [time in freq1] [time in freq2] [time in freq3] ... + * [uid2]: [time in freq1] [time in freq2] [time in freq3] ... + * ... + * + * This provides the times a UID's processes spent executing at each different cpu frequency. + * The file contains a monotonically increasing count of time for a single boot. This class + * maintains the previous results of a call to {@link #readDelta} in order to provide a proper + * delta. + */ +public class KernelUidCpuFreqTimeReader { + private static final String TAG = "KernelUidCpuFreqTimeReader"; + private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state"; + + public interface Callback { + void onCpuFreqs(long[] cpuFreqs); + void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs); + } + + private long[] mCpuFreqs; + private int mCpuFreqsCount; + + private SparseArray<long[]> mLastUidCpuFreqTimeMs = new SparseArray<>(); + + public void readDelta(@Nullable Callback callback) { + try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) { + readDelta(reader, callback); + } catch (IOException e) { + Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e); + } + } + + @VisibleForTesting + public void readDelta(BufferedReader reader, @Nullable Callback callback) throws IOException { + String line = reader.readLine(); + if (line == null) { + return; + } + readCpuFreqs(line, callback); + while ((line = reader.readLine()) != null) { + final int index = line.indexOf(' '); + final int uid = Integer.parseInt(line.substring(0, index - 1), 10); + readTimesForUid(uid, line.substring(index + 1, line.length()), callback); + } + } + + private void readTimesForUid(int uid, String line, Callback callback) { + long[] uidTimeMs = mLastUidCpuFreqTimeMs.get(uid); + if (uidTimeMs == null) { + uidTimeMs = new long[mCpuFreqsCount]; + mLastUidCpuFreqTimeMs.put(uid, uidTimeMs); + } + final String[] timesStr = line.split(" "); + final int size = timesStr.length; + if (size != uidTimeMs.length) { + Slog.e(TAG, "No. of readings don't match cpu freqs, readings: " + size + + " cpuFreqsCount: " + uidTimeMs.length); + return; + } + final long[] deltaUidTimeMs = new long[size]; + for (int i = 0; i < size; ++i) { + // Times read will be in units of 10ms + final long totalTimeMs = Long.parseLong(timesStr[i], 10) * 10; + deltaUidTimeMs[i] = totalTimeMs - uidTimeMs[i]; + uidTimeMs[i] = totalTimeMs; + } + if (callback != null) { + callback.onUidCpuFreqTime(uid, deltaUidTimeMs); + } + } + + private void readCpuFreqs(String line, Callback callback) { + if (mCpuFreqs == null) { + final String[] freqStr = line.split(" "); + // First item would be "uid:" which needs to be ignored + mCpuFreqsCount = freqStr.length - 1; + mCpuFreqs = new long[mCpuFreqsCount]; + for (int i = 0; i < mCpuFreqsCount; ++i) { + mCpuFreqs[i] = Long.parseLong(freqStr[i + 1], 10); + } + } + if (callback != null) { + callback.onCpuFreqs(mCpuFreqs); + } + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java new file mode 100644 index 000000000000..ad8221b470ae --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2017 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 static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.BufferedReader; + +/** + * Test class for {@link KernelUidCpuFreqTimeReader}. + * + * To run the tests, use + * + * runtest -c com.android.internal.os.KernelUidCpuFreqTimeReaderTest frameworks-core + * + * or the following steps: + * + * Build: m FrameworksCoreTests + * Install: adb install -r \ + * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk + * Run: adb shell am instrument -e class com.android.internal.os.KernelUidCpuFreqTimeReaderTest -w \ + * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class KernelUidCpuFreqTimeReaderTest { + @Mock private BufferedReader mBufferedReader; + @Mock private KernelUidCpuFreqTimeReader.Callback mCallback; + + private KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mKernelUidCpuFreqTimeReader = new KernelUidCpuFreqTimeReader(); + } + + @Test + public void testReadDelta() throws Exception { + final long[] freqs = {1, 12, 123, 1234, 12345, 123456}; + final int[] uids = {1, 22, 333, 4444, 5555}; + final long[][] times = new long[uids.length][freqs.length]; + for (int i = 0; i < uids.length; ++i) { + for (int j = 0; j < freqs.length; ++j) { + times[i][j] = uids[i] * freqs[j] * 10; + } + } + final String[] uidsTimesLines = getUidTimesLines(uids, times); + final String[] lines = new String[uidsTimesLines.length + 1]; + System.arraycopy(uidsTimesLines, 0, lines, 0, uidsTimesLines.length); + lines[uidsTimesLines.length] = null; + when(mBufferedReader.readLine()) + .thenReturn(getFreqsLine(freqs), lines); + mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback); + verify(mCallback).onCpuFreqs(freqs); + for (int i = 0; i < uids.length; ++i) { + verify(mCallback).onUidCpuFreqTime(uids[i], times[i]); + } + verifyNoMoreInteractions(mCallback); + } + + private String getFreqsLine(long[] freqs) { + final StringBuilder sb = new StringBuilder(); + sb.append("uid:"); + for (int i = 0; i < freqs.length; ++i) { + sb.append(" " + freqs[i]); + } + return sb.toString(); + } + + private String[] getUidTimesLines(int[] uids, long[][] times) { + final String[] lines = new String[uids.length]; + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < uids.length; ++i) { + sb.setLength(0); + sb.append(uids[i] + ":"); + for (int j = 0; j < times[i].length; ++j) { + sb.append(" " + times[i][j] / 10); + } + lines[i] = sb.toString(); + } + return lines; + } +} |