Loading core/java/android/os/BatteryStats.java +106 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.os; import android.app.ActivityManager; import android.app.job.JobParameters; import android.content.Context; import android.content.pm.ApplicationInfo; Loading @@ -33,6 +34,7 @@ import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.view.Display; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; Loading Loading @@ -327,7 +329,8 @@ public abstract class BatteryStats implements Parcelable { * * Other types might include times spent in foreground, background etc. */ private final String UID_TIMES_TYPE_ALL = "A"; @VisibleForTesting public static final String UID_TIMES_TYPE_ALL = "A"; /** * State for keeping track of counting information. Loading Loading @@ -506,6 +509,31 @@ public abstract class BatteryStats implements Parcelable { public abstract void logState(Printer pw, String prefix); } /** * Maps the ActivityManager procstate into corresponding BatteryStats procstate. */ public static int mapToInternalProcessState(int procState) { if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) { return ActivityManager.PROCESS_STATE_NONEXISTENT; } else if (procState == ActivityManager.PROCESS_STATE_TOP) { return Uid.PROCESS_STATE_TOP; } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { // Persistent and other foreground states go here. return Uid.PROCESS_STATE_FOREGROUND_SERVICE; } else if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { // Persistent and other foreground states go here. return Uid.PROCESS_STATE_FOREGROUND; } else if (procState <= ActivityManager.PROCESS_STATE_RECEIVER) { return Uid.PROCESS_STATE_BACKGROUND; } else if (procState <= ActivityManager.PROCESS_STATE_TOP_SLEEPING) { return Uid.PROCESS_STATE_TOP_SLEEPING; } else if (procState <= ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) { return Uid.PROCESS_STATE_HEAVY_WEIGHT; } else { return Uid.PROCESS_STATE_CACHED; } } /** * The statistics associated with a particular uid. */ Loading Loading @@ -645,6 +673,15 @@ public abstract class BatteryStats implements Parcelable { public abstract long[] getCpuFreqTimes(int which); public abstract long[] getScreenOffCpuFreqTimes(int which); /** * Returns cpu times of an uid at a particular process state. */ public abstract long[] getCpuFreqTimes(int which, int procState); /** * Returns cpu times of an uid while the screen if off at a particular process state. */ public abstract long[] getScreenOffCpuFreqTimes(int which, int procState); // 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. Loading Loading @@ -689,11 +726,32 @@ public abstract class BatteryStats implements Parcelable { */ public static final int NUM_PROCESS_STATE = 7; // Used in dump static final String[] PROCESS_STATE_NAMES = { "Top", "Fg Service", "Foreground", "Background", "Top Sleeping", "Heavy Weight", "Cached" }; // Used in checkin dump @VisibleForTesting public static final String[] UID_PROCESS_TYPES = { "T", // TOP "FS", // FOREGROUND_SERVICE "F", // FOREGROUND "B", // BACKGROUND "TS", // TOP_SLEEPING "HW", // HEAVY_WEIGHT "C" // CACHED }; /** * When the process exits one of these states, we need to make sure cpu time in this state * is not attributed to any non-critical process states. */ public static final int[] CRITICAL_PROC_STATES = { PROCESS_STATE_TOP, PROCESS_STATE_FOREGROUND_SERVICE, PROCESS_STATE_FOREGROUND }; public abstract long getProcessStateTime(int state, long elapsedRealtimeUs, int which); public abstract Timer getProcessStateTimer(int state); Loading Loading @@ -4002,6 +4060,29 @@ public abstract class BatteryStats implements Parcelable { dumpLine(pw, uid, category, CPU_TIMES_AT_FREQ_DATA, UID_TIMES_TYPE_ALL, cpuFreqTimeMs.length, sb.toString()); } for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) { final long[] timesMs = u.getCpuFreqTimes(which, procState); if (timesMs != null && timesMs.length == cpuFreqs.length) { sb.setLength(0); for (int i = 0; i < timesMs.length; ++i) { sb.append((i == 0 ? "" : ",") + timesMs[i]); } final long[] screenOffTimesMs = u.getScreenOffCpuFreqTimes( which, procState); if (screenOffTimesMs != null) { for (int i = 0; i < screenOffTimesMs.length; ++i) { sb.append("," + screenOffTimesMs[i]); } } else { for (int i = 0; i < timesMs.length; ++i) { sb.append(",0"); } } dumpLine(pw, uid, category, CPU_TIMES_AT_FREQ_DATA, Uid.UID_PROCESS_TYPES[procState], timesMs.length, sb.toString()); } } } final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats Loading Loading @@ -5612,6 +5693,30 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); } for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) { final long[] cpuTimes = u.getCpuFreqTimes(which, procState); if (cpuTimes != null) { sb.setLength(0); sb.append(" Cpu times per freq at state " + Uid.PROCESS_STATE_NAMES[procState] + ":"); for (int i = 0; i < cpuTimes.length; ++i) { sb.append(" " + cpuTimes[i]); } pw.println(sb.toString()); } final long[] screenOffCpuTimes = u.getScreenOffCpuFreqTimes(which, procState); if (screenOffCpuTimes != null) { sb.setLength(0); sb.append(" Screen-off cpu times per freq at state " + Uid.PROCESS_STATE_NAMES[procState] + ":"); for (int i = 0; i < screenOffCpuTimes.length; ++i) { sb.append(" " + screenOffCpuTimes[i]); } pw.println(sb.toString()); } } final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); for (int ipr=processStats.size()-1; ipr>=0; ipr--) { Loading core/java/com/android/internal/os/BatteryStatsImpl.java +387 −43 File changed.Preview size limit exceeded, changes collapsed. Show changes core/java/com/android/internal/os/KernelSingleUidTimeReader.java 0 → 100644 +204 −0 Original line number Diff line number Diff line /* * 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 com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import android.annotation.NonNull; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; @VisibleForTesting(visibility = PACKAGE) public class KernelSingleUidTimeReader { private final String TAG = KernelUidCpuFreqTimeReader.class.getName(); private final boolean DBG = false; private final String PROC_FILE_DIR = "/proc/uid/"; private final String PROC_FILE_NAME = "/time_in_state"; @VisibleForTesting public static final int TOTAL_READ_ERROR_COUNT = 5; @GuardedBy("this") private final int mCpuFreqsCount; @GuardedBy("this") private final SparseArray<long[]> mLastUidCpuTimeMs = new SparseArray<>(); @GuardedBy("this") private int mReadErrorCounter; @GuardedBy("this") private boolean mSingleUidCpuTimesAvailable = true; private final Injector mInjector; KernelSingleUidTimeReader(int cpuFreqsCount) { this(cpuFreqsCount, new Injector()); } public KernelSingleUidTimeReader(int cpuFreqsCount, Injector injector) { mInjector = injector; mCpuFreqsCount = cpuFreqsCount; if (mCpuFreqsCount == 0) { mSingleUidCpuTimesAvailable = false; } } public boolean singleUidCpuTimesAvailable() { return mSingleUidCpuTimesAvailable; } public long[] readDeltaMs(int uid) { synchronized (this) { if (!mSingleUidCpuTimesAvailable) { return null; } // Read total cpu times from the proc file. final String procFile = new StringBuilder(PROC_FILE_DIR) .append(uid) .append(PROC_FILE_NAME).toString(); final long[] cpuTimesMs = new long[mCpuFreqsCount]; try { final byte[] data = mInjector.readData(procFile); final ByteBuffer buffer = ByteBuffer.wrap(data); buffer.order(ByteOrder.nativeOrder()); for (int i = 0; i < mCpuFreqsCount; ++i) { // Times read will be in units of 10ms cpuTimesMs[i] = buffer.getLong() * 10; } } catch (Exception e) { if (++mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) { mSingleUidCpuTimesAvailable = false; } if (DBG) Slog.e(TAG, "Some error occured while reading " + procFile, e); return null; } return computeDelta(uid, cpuTimesMs); } } /** * Compute and return cpu times delta of an uid using previously read cpu times and * {@param latestCpuTimesMs}. * * @return delta of cpu times if at least one of the cpu time at a freq is +ve, otherwise null. */ public long[] computeDelta(int uid, @NonNull long[] latestCpuTimesMs) { synchronized (this) { if (!mSingleUidCpuTimesAvailable) { return null; } // Subtract the last read cpu times to get deltas. final long[] lastCpuTimesMs = mLastUidCpuTimeMs.get(uid); final long[] deltaTimesMs = getDeltaLocked(lastCpuTimesMs, latestCpuTimesMs); if (deltaTimesMs == null) { if (DBG) Slog.e(TAG, "Malformed data read for uid=" + uid + "; last=" + Arrays.toString(lastCpuTimesMs) + "; latest=" + Arrays.toString(latestCpuTimesMs)); return null; } // If all elements are zero, return null to avoid unnecessary work on the caller side. boolean hasNonZero = false; for (int i = deltaTimesMs.length - 1; i >= 0; --i) { if (deltaTimesMs[i] > 0) { hasNonZero = true; break; } } if (hasNonZero) { mLastUidCpuTimeMs.put(uid, latestCpuTimesMs); return deltaTimesMs; } else { return null; } } } /** * Returns null if the latest cpu times are not valid**, otherwise delta of * {@param latestCpuTimesMs} and {@param lastCpuTimesMs}. * * **latest cpu times are considered valid if all the cpu times are +ve and * greater than or equal to previously read cpu times. */ @GuardedBy("this") @VisibleForTesting(visibility = PACKAGE) public long[] getDeltaLocked(long[] lastCpuTimesMs, @NonNull long[] latestCpuTimesMs) { for (int i = latestCpuTimesMs.length - 1; i >= 0; --i) { if (latestCpuTimesMs[i] < 0) { return null; } } if (lastCpuTimesMs == null) { return latestCpuTimesMs; } final long[] deltaTimesMs = new long[latestCpuTimesMs.length]; for (int i = latestCpuTimesMs.length - 1; i >= 0; --i) { deltaTimesMs[i] = latestCpuTimesMs[i] - lastCpuTimesMs[i]; if (deltaTimesMs[i] < 0) { return null; } } return deltaTimesMs; } public void removeUid(int uid) { synchronized (this) { mLastUidCpuTimeMs.delete(uid); } } public void removeUidsInRange(int startUid, int endUid) { if (endUid < startUid) { return; } synchronized (this) { mLastUidCpuTimeMs.put(startUid, null); mLastUidCpuTimeMs.put(endUid, null); final int startIdx = mLastUidCpuTimeMs.indexOfKey(startUid); final int endIdx = mLastUidCpuTimeMs.indexOfKey(endUid); mLastUidCpuTimeMs.removeAtRange(startIdx, endIdx - startIdx + 1); } } @VisibleForTesting public static class Injector { public byte[] readData(String procFile) throws IOException { return Files.readAllBytes(Paths.get(procFile)); } } @VisibleForTesting public SparseArray<long[]> getLastUidCpuTimeMs() { return mLastUidCpuTimeMs; } @VisibleForTesting public void setSingleUidCpuTimesAvailable(boolean singleUidCpuTimesAvailable) { mSingleUidCpuTimesAvailable = singleUidCpuTimesAvailable; } } No newline at end of file core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java +14 −5 Original line number Diff line number Diff line Loading @@ -66,13 +66,21 @@ public class KernelUidCpuFreqTimeReader { // start reading) and if it is not available, we simply ignore further read requests. private static final int TOTAL_READ_ERROR_COUNT = 5; private int mReadErrorCounter; private boolean mProcFileAvailable; private boolean mPerClusterTimesAvailable; private boolean mAllUidTimesAvailable = true; public boolean perClusterTimesAvailable() { return mPerClusterTimesAvailable; } public boolean allUidTimesAvailable() { return mAllUidTimesAvailable; } public SparseArray<long[]> getAllUidCpuFreqTimeMs() { return mLastUidCpuFreqTimeMs; } public long[] readFreqs(@NonNull PowerProfile powerProfile) { checkNotNull(powerProfile); Loading @@ -80,15 +88,16 @@ public class KernelUidCpuFreqTimeReader { // No need to read cpu freqs more than once. return mCpuFreqs; } if (!mProcFileAvailable && mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) { if (!mAllUidTimesAvailable) { return null; } final int oldMask = StrictMode.allowThreadDiskReadsMask(); try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) { mProcFileAvailable = true; return readFreqs(reader, powerProfile); } catch (IOException e) { mReadErrorCounter++; if (++mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) { mAllUidTimesAvailable = false; } Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e); return null; } finally { Loading @@ -107,7 +116,7 @@ public class KernelUidCpuFreqTimeReader { } public void readDelta(@Nullable Callback callback) { if (!mProcFileAvailable) { if (mCpuFreqs == null) { return; } final int oldMask = StrictMode.allowThreadDiskReadsMask(); Loading core/tests/coretests/BstatsTestApp/AndroidManifest.xml +6 −0 Original line number Diff line number Diff line Loading @@ -17,9 +17,15 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.coretests.apps.bstatstestapp"> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="25"/> <application> <activity android:name=".TestActivity" android:exported="true" /> <service android:name=".TestService" android:exported="true" /> <service android:name=".IsolatedTestService" android:exported="true" android:isolatedProcess="true" /> Loading Loading
core/java/android/os/BatteryStats.java +106 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.os; import android.app.ActivityManager; import android.app.job.JobParameters; import android.content.Context; import android.content.pm.ApplicationInfo; Loading @@ -33,6 +34,7 @@ import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.view.Display; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; Loading Loading @@ -327,7 +329,8 @@ public abstract class BatteryStats implements Parcelable { * * Other types might include times spent in foreground, background etc. */ private final String UID_TIMES_TYPE_ALL = "A"; @VisibleForTesting public static final String UID_TIMES_TYPE_ALL = "A"; /** * State for keeping track of counting information. Loading Loading @@ -506,6 +509,31 @@ public abstract class BatteryStats implements Parcelable { public abstract void logState(Printer pw, String prefix); } /** * Maps the ActivityManager procstate into corresponding BatteryStats procstate. */ public static int mapToInternalProcessState(int procState) { if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) { return ActivityManager.PROCESS_STATE_NONEXISTENT; } else if (procState == ActivityManager.PROCESS_STATE_TOP) { return Uid.PROCESS_STATE_TOP; } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { // Persistent and other foreground states go here. return Uid.PROCESS_STATE_FOREGROUND_SERVICE; } else if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { // Persistent and other foreground states go here. return Uid.PROCESS_STATE_FOREGROUND; } else if (procState <= ActivityManager.PROCESS_STATE_RECEIVER) { return Uid.PROCESS_STATE_BACKGROUND; } else if (procState <= ActivityManager.PROCESS_STATE_TOP_SLEEPING) { return Uid.PROCESS_STATE_TOP_SLEEPING; } else if (procState <= ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) { return Uid.PROCESS_STATE_HEAVY_WEIGHT; } else { return Uid.PROCESS_STATE_CACHED; } } /** * The statistics associated with a particular uid. */ Loading Loading @@ -645,6 +673,15 @@ public abstract class BatteryStats implements Parcelable { public abstract long[] getCpuFreqTimes(int which); public abstract long[] getScreenOffCpuFreqTimes(int which); /** * Returns cpu times of an uid at a particular process state. */ public abstract long[] getCpuFreqTimes(int which, int procState); /** * Returns cpu times of an uid while the screen if off at a particular process state. */ public abstract long[] getScreenOffCpuFreqTimes(int which, int procState); // 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. Loading Loading @@ -689,11 +726,32 @@ public abstract class BatteryStats implements Parcelable { */ public static final int NUM_PROCESS_STATE = 7; // Used in dump static final String[] PROCESS_STATE_NAMES = { "Top", "Fg Service", "Foreground", "Background", "Top Sleeping", "Heavy Weight", "Cached" }; // Used in checkin dump @VisibleForTesting public static final String[] UID_PROCESS_TYPES = { "T", // TOP "FS", // FOREGROUND_SERVICE "F", // FOREGROUND "B", // BACKGROUND "TS", // TOP_SLEEPING "HW", // HEAVY_WEIGHT "C" // CACHED }; /** * When the process exits one of these states, we need to make sure cpu time in this state * is not attributed to any non-critical process states. */ public static final int[] CRITICAL_PROC_STATES = { PROCESS_STATE_TOP, PROCESS_STATE_FOREGROUND_SERVICE, PROCESS_STATE_FOREGROUND }; public abstract long getProcessStateTime(int state, long elapsedRealtimeUs, int which); public abstract Timer getProcessStateTimer(int state); Loading Loading @@ -4002,6 +4060,29 @@ public abstract class BatteryStats implements Parcelable { dumpLine(pw, uid, category, CPU_TIMES_AT_FREQ_DATA, UID_TIMES_TYPE_ALL, cpuFreqTimeMs.length, sb.toString()); } for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) { final long[] timesMs = u.getCpuFreqTimes(which, procState); if (timesMs != null && timesMs.length == cpuFreqs.length) { sb.setLength(0); for (int i = 0; i < timesMs.length; ++i) { sb.append((i == 0 ? "" : ",") + timesMs[i]); } final long[] screenOffTimesMs = u.getScreenOffCpuFreqTimes( which, procState); if (screenOffTimesMs != null) { for (int i = 0; i < screenOffTimesMs.length; ++i) { sb.append("," + screenOffTimesMs[i]); } } else { for (int i = 0; i < timesMs.length; ++i) { sb.append(",0"); } } dumpLine(pw, uid, category, CPU_TIMES_AT_FREQ_DATA, Uid.UID_PROCESS_TYPES[procState], timesMs.length, sb.toString()); } } } final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats Loading Loading @@ -5612,6 +5693,30 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); } for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) { final long[] cpuTimes = u.getCpuFreqTimes(which, procState); if (cpuTimes != null) { sb.setLength(0); sb.append(" Cpu times per freq at state " + Uid.PROCESS_STATE_NAMES[procState] + ":"); for (int i = 0; i < cpuTimes.length; ++i) { sb.append(" " + cpuTimes[i]); } pw.println(sb.toString()); } final long[] screenOffCpuTimes = u.getScreenOffCpuFreqTimes(which, procState); if (screenOffCpuTimes != null) { sb.setLength(0); sb.append(" Screen-off cpu times per freq at state " + Uid.PROCESS_STATE_NAMES[procState] + ":"); for (int i = 0; i < screenOffCpuTimes.length; ++i) { sb.append(" " + screenOffCpuTimes[i]); } pw.println(sb.toString()); } } final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); for (int ipr=processStats.size()-1; ipr>=0; ipr--) { Loading
core/java/com/android/internal/os/BatteryStatsImpl.java +387 −43 File changed.Preview size limit exceeded, changes collapsed. Show changes
core/java/com/android/internal/os/KernelSingleUidTimeReader.java 0 → 100644 +204 −0 Original line number Diff line number Diff line /* * 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 com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import android.annotation.NonNull; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; @VisibleForTesting(visibility = PACKAGE) public class KernelSingleUidTimeReader { private final String TAG = KernelUidCpuFreqTimeReader.class.getName(); private final boolean DBG = false; private final String PROC_FILE_DIR = "/proc/uid/"; private final String PROC_FILE_NAME = "/time_in_state"; @VisibleForTesting public static final int TOTAL_READ_ERROR_COUNT = 5; @GuardedBy("this") private final int mCpuFreqsCount; @GuardedBy("this") private final SparseArray<long[]> mLastUidCpuTimeMs = new SparseArray<>(); @GuardedBy("this") private int mReadErrorCounter; @GuardedBy("this") private boolean mSingleUidCpuTimesAvailable = true; private final Injector mInjector; KernelSingleUidTimeReader(int cpuFreqsCount) { this(cpuFreqsCount, new Injector()); } public KernelSingleUidTimeReader(int cpuFreqsCount, Injector injector) { mInjector = injector; mCpuFreqsCount = cpuFreqsCount; if (mCpuFreqsCount == 0) { mSingleUidCpuTimesAvailable = false; } } public boolean singleUidCpuTimesAvailable() { return mSingleUidCpuTimesAvailable; } public long[] readDeltaMs(int uid) { synchronized (this) { if (!mSingleUidCpuTimesAvailable) { return null; } // Read total cpu times from the proc file. final String procFile = new StringBuilder(PROC_FILE_DIR) .append(uid) .append(PROC_FILE_NAME).toString(); final long[] cpuTimesMs = new long[mCpuFreqsCount]; try { final byte[] data = mInjector.readData(procFile); final ByteBuffer buffer = ByteBuffer.wrap(data); buffer.order(ByteOrder.nativeOrder()); for (int i = 0; i < mCpuFreqsCount; ++i) { // Times read will be in units of 10ms cpuTimesMs[i] = buffer.getLong() * 10; } } catch (Exception e) { if (++mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) { mSingleUidCpuTimesAvailable = false; } if (DBG) Slog.e(TAG, "Some error occured while reading " + procFile, e); return null; } return computeDelta(uid, cpuTimesMs); } } /** * Compute and return cpu times delta of an uid using previously read cpu times and * {@param latestCpuTimesMs}. * * @return delta of cpu times if at least one of the cpu time at a freq is +ve, otherwise null. */ public long[] computeDelta(int uid, @NonNull long[] latestCpuTimesMs) { synchronized (this) { if (!mSingleUidCpuTimesAvailable) { return null; } // Subtract the last read cpu times to get deltas. final long[] lastCpuTimesMs = mLastUidCpuTimeMs.get(uid); final long[] deltaTimesMs = getDeltaLocked(lastCpuTimesMs, latestCpuTimesMs); if (deltaTimesMs == null) { if (DBG) Slog.e(TAG, "Malformed data read for uid=" + uid + "; last=" + Arrays.toString(lastCpuTimesMs) + "; latest=" + Arrays.toString(latestCpuTimesMs)); return null; } // If all elements are zero, return null to avoid unnecessary work on the caller side. boolean hasNonZero = false; for (int i = deltaTimesMs.length - 1; i >= 0; --i) { if (deltaTimesMs[i] > 0) { hasNonZero = true; break; } } if (hasNonZero) { mLastUidCpuTimeMs.put(uid, latestCpuTimesMs); return deltaTimesMs; } else { return null; } } } /** * Returns null if the latest cpu times are not valid**, otherwise delta of * {@param latestCpuTimesMs} and {@param lastCpuTimesMs}. * * **latest cpu times are considered valid if all the cpu times are +ve and * greater than or equal to previously read cpu times. */ @GuardedBy("this") @VisibleForTesting(visibility = PACKAGE) public long[] getDeltaLocked(long[] lastCpuTimesMs, @NonNull long[] latestCpuTimesMs) { for (int i = latestCpuTimesMs.length - 1; i >= 0; --i) { if (latestCpuTimesMs[i] < 0) { return null; } } if (lastCpuTimesMs == null) { return latestCpuTimesMs; } final long[] deltaTimesMs = new long[latestCpuTimesMs.length]; for (int i = latestCpuTimesMs.length - 1; i >= 0; --i) { deltaTimesMs[i] = latestCpuTimesMs[i] - lastCpuTimesMs[i]; if (deltaTimesMs[i] < 0) { return null; } } return deltaTimesMs; } public void removeUid(int uid) { synchronized (this) { mLastUidCpuTimeMs.delete(uid); } } public void removeUidsInRange(int startUid, int endUid) { if (endUid < startUid) { return; } synchronized (this) { mLastUidCpuTimeMs.put(startUid, null); mLastUidCpuTimeMs.put(endUid, null); final int startIdx = mLastUidCpuTimeMs.indexOfKey(startUid); final int endIdx = mLastUidCpuTimeMs.indexOfKey(endUid); mLastUidCpuTimeMs.removeAtRange(startIdx, endIdx - startIdx + 1); } } @VisibleForTesting public static class Injector { public byte[] readData(String procFile) throws IOException { return Files.readAllBytes(Paths.get(procFile)); } } @VisibleForTesting public SparseArray<long[]> getLastUidCpuTimeMs() { return mLastUidCpuTimeMs; } @VisibleForTesting public void setSingleUidCpuTimesAvailable(boolean singleUidCpuTimesAvailable) { mSingleUidCpuTimesAvailable = singleUidCpuTimesAvailable; } } No newline at end of file
core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java +14 −5 Original line number Diff line number Diff line Loading @@ -66,13 +66,21 @@ public class KernelUidCpuFreqTimeReader { // start reading) and if it is not available, we simply ignore further read requests. private static final int TOTAL_READ_ERROR_COUNT = 5; private int mReadErrorCounter; private boolean mProcFileAvailable; private boolean mPerClusterTimesAvailable; private boolean mAllUidTimesAvailable = true; public boolean perClusterTimesAvailable() { return mPerClusterTimesAvailable; } public boolean allUidTimesAvailable() { return mAllUidTimesAvailable; } public SparseArray<long[]> getAllUidCpuFreqTimeMs() { return mLastUidCpuFreqTimeMs; } public long[] readFreqs(@NonNull PowerProfile powerProfile) { checkNotNull(powerProfile); Loading @@ -80,15 +88,16 @@ public class KernelUidCpuFreqTimeReader { // No need to read cpu freqs more than once. return mCpuFreqs; } if (!mProcFileAvailable && mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) { if (!mAllUidTimesAvailable) { return null; } final int oldMask = StrictMode.allowThreadDiskReadsMask(); try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) { mProcFileAvailable = true; return readFreqs(reader, powerProfile); } catch (IOException e) { mReadErrorCounter++; if (++mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) { mAllUidTimesAvailable = false; } Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e); return null; } finally { Loading @@ -107,7 +116,7 @@ public class KernelUidCpuFreqTimeReader { } public void readDelta(@Nullable Callback callback) { if (!mProcFileAvailable) { if (mCpuFreqs == null) { return; } final int oldMask = StrictMode.allowThreadDiskReadsMask(); Loading
core/tests/coretests/BstatsTestApp/AndroidManifest.xml +6 −0 Original line number Diff line number Diff line Loading @@ -17,9 +17,15 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.coretests.apps.bstatstestapp"> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="25"/> <application> <activity android:name=".TestActivity" android:exported="true" /> <service android:name=".TestService" android:exported="true" /> <service android:name=".IsolatedTestService" android:exported="true" android:isolatedProcess="true" /> Loading