13d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma/* 23d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma * Copyright (C) 2017 The Android Open Source Project 33d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma * 43d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma * Licensed under the Apache License, Version 2.0 (the "License"); 53d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma * you may not use this file except in compliance with the License. 63d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma * You may obtain a copy of the License at 73d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma * 83d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma * http://www.apache.org/licenses/LICENSE-2.0 93d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma * 103d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma * Unless required by applicable law or agreed to in writing, software 113d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma * distributed under the License is distributed on an "AS IS" BASIS, 123d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma * See the License for the specific language governing permissions and 143d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma * limitations under the License. 153d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma */ 163d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma 173d422c37b7f2caf364febc556e17b43c40c56be6Mike Mapackage com.android.internal.os; 183d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma 193d422c37b7f2caf364febc556e17b43c40c56be6Mike Maimport android.annotation.Nullable; 203d422c37b7f2caf364febc556e17b43c40c56be6Mike Maimport android.util.Slog; 213d422c37b7f2caf364febc556e17b43c40c56be6Mike Maimport android.util.SparseArray; 223d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma 233d422c37b7f2caf364febc556e17b43c40c56be6Mike Maimport com.android.internal.annotations.VisibleForTesting; 243d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma 252ab014426647bbc8960fdb4dadfe480b9806676eMike Maimport java.nio.ByteBuffer; 262ab014426647bbc8960fdb4dadfe480b9806676eMike Maimport java.nio.IntBuffer; 27234d1828ca4a706676ad6f3fa0629959c74297c7Mike Maimport java.util.function.Consumer; 283d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma 293d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma/** 302ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * Reads binary proc file /proc/uid_cpupower/concurrent_policy_time and reports CPU cluster times 312ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * to BatteryStats to compute cluster power. See 322ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * {@link PowerProfile#getAveragePowerForCpuCluster(int)}. 332ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * 342ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * concurrent_policy_time is an array of u32's in the following format: 352ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * [n, x0, ..., xn, uid0, time0a, time0b, ..., time0n, 362ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * uid1, time1a, time1b, ..., time1n, 372ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * uid2, time2a, time2b, ..., time2n, etc.] 382ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * where n is the number of policies 392ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * xi is the number cpus on a particular policy 402ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * Each uidX is followed by x0 time entries corresponding to the time UID X spent on cluster0 412ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * running concurrently with 0, 1, 2, ..., x0 - 1 other processes, then followed by x1, ..., xn 422ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * time entries. 432ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * 443d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma * The file contains a monotonically increasing count of time for a single boot. This class 452ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * maintains the previous results of a call to {@link #readDelta} in order to provide a 462ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * proper delta. 472ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * 482ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * This class uses a throttler to reject any {@link #readDelta} call within 492ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader}, 502ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * which has a shorter throttle interval and returns cached result from last read when the request 512ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * is throttled. 522ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * 5369d8b3e0508de7e181f70a3322be626b1527ee0eMike Ma * This class is NOT thread-safe and NOT designed to be accessed by more than one caller since each 5469d8b3e0508de7e181f70a3322be626b1527ee0eMike Ma * caller has its own view of delta. 553d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma */ 5669d8b3e0508de7e181f70a3322be626b1527ee0eMike Mapublic class KernelUidCpuClusterTimeReader extends 5769d8b3e0508de7e181f70a3322be626b1527ee0eMike Ma KernelUidCpuTimeReaderBase<KernelUidCpuClusterTimeReader.Callback> { 5869d8b3e0508de7e181f70a3322be626b1527ee0eMike Ma private static final String TAG = KernelUidCpuClusterTimeReader.class.getSimpleName(); 592ab014426647bbc8960fdb4dadfe480b9806676eMike Ma 602ab014426647bbc8960fdb4dadfe480b9806676eMike Ma private final KernelCpuProcReader mProcReader; 612ab014426647bbc8960fdb4dadfe480b9806676eMike Ma private SparseArray<double[]> mLastUidPolicyTimeMs = new SparseArray<>(); 623d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma 632ab014426647bbc8960fdb4dadfe480b9806676eMike Ma private int mNumClusters = -1; 642ab014426647bbc8960fdb4dadfe480b9806676eMike Ma private int mNumCores; 652ab014426647bbc8960fdb4dadfe480b9806676eMike Ma private int[] mNumCoresOnCluster; 662ab014426647bbc8960fdb4dadfe480b9806676eMike Ma 672ab014426647bbc8960fdb4dadfe480b9806676eMike Ma private double[] mCurTime; // Reuse to avoid GC. 682ab014426647bbc8960fdb4dadfe480b9806676eMike Ma private long[] mDeltaTime; // Reuse to avoid GC. 69ec67661bdfe1814039f625575176678219acc7b1Chenjie Yu private long[] mCurTimeRounded; // Reuse to avoid GC. 703d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma 7169d8b3e0508de7e181f70a3322be626b1527ee0eMike Ma public interface Callback extends KernelUidCpuTimeReaderBase.Callback { 723d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma /** 732ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * Notifies when new data is available. 742ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * 752ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * @param uid uid int 762ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * @param cpuClusterTimeMs an array of times spent by this uid on corresponding clusters. 772ab014426647bbc8960fdb4dadfe480b9806676eMike Ma * The array index is the cluster index. 783d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma */ 792ab014426647bbc8960fdb4dadfe480b9806676eMike Ma void onUidCpuPolicyTime(int uid, long[] cpuClusterTimeMs); 803d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma } 813d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma 822ab014426647bbc8960fdb4dadfe480b9806676eMike Ma public KernelUidCpuClusterTimeReader() { 832ab014426647bbc8960fdb4dadfe480b9806676eMike Ma mProcReader = KernelCpuProcReader.getClusterTimeReaderInstance(); 843d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma } 853d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma 862ab014426647bbc8960fdb4dadfe480b9806676eMike Ma @VisibleForTesting 872ab014426647bbc8960fdb4dadfe480b9806676eMike Ma public KernelUidCpuClusterTimeReader(KernelCpuProcReader procReader) { 882ab014426647bbc8960fdb4dadfe480b9806676eMike Ma mProcReader = procReader; 893d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma } 903d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma 9169d8b3e0508de7e181f70a3322be626b1527ee0eMike Ma @Override 9269d8b3e0508de7e181f70a3322be626b1527ee0eMike Ma protected void readDeltaImpl(@Nullable Callback cb) { 93234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma readImpl((buf) -> { 94234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma int uid = buf.get(); 95234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma double[] lastTimes = mLastUidPolicyTimeMs.get(uid); 96234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma if (lastTimes == null) { 97234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma lastTimes = new double[mNumClusters]; 98234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma mLastUidPolicyTimeMs.put(uid, lastTimes); 99234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma } 100234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma if (!sumClusterTime(buf, mCurTime)) { 101234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma return; 102234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma } 103234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma boolean valid = true; 104234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma boolean notify = false; 105234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma for (int i = 0; i < mNumClusters; i++) { 106234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma mDeltaTime[i] = (long) (mCurTime[i] - lastTimes[i]); 107234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma if (mDeltaTime[i] < 0) { 108234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma Slog.e(TAG, "Negative delta from cluster time proc: " + mDeltaTime[i]); 109234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma valid = false; 110234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma } 111234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma notify |= mDeltaTime[i] > 0; 112234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma } 113234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma if (notify && valid) { 114234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters); 115234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma if (cb != null) { 116234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma cb.onUidCpuPolicyTime(uid, mDeltaTime); 117234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma } 118234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma } 119234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma }); 120234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma } 121234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma 122234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma public void readAbsolute(Callback callback) { 123234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma readImpl((buf) -> { 124234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma int uid = buf.get(); 125234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma if (sumClusterTime(buf, mCurTime)) { 126234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma for (int i = 0; i < mNumClusters; i++) { 127234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma mCurTimeRounded[i] = (long) mCurTime[i]; 128234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma } 129234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma callback.onUidCpuPolicyTime(uid, mCurTimeRounded); 130234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma } 131234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma }); 132234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma } 133234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma 134234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma private boolean sumClusterTime(IntBuffer buffer, double[] clusterTime) { 135234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma boolean valid = true; 136234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma for (int i = 0; i < mNumClusters; i++) { 137234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma clusterTime[i] = 0; 138234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma for (int j = 1; j <= mNumCoresOnCluster[i]; j++) { 139234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma int time = buffer.get(); 140234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma if (time < 0) { 141234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma Slog.e(TAG, "Negative time from cluster time proc: " + time); 142234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma valid = false; 143234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma } 144234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma clusterTime[i] += (double) time * 10 / j; // Unit is 10ms. 145234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma } 146234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma } 147234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma return valid; 148234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma } 149234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma 150234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma /** 151234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last 152234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma * seen results while processing the buffer, while readAbsolute returns the absolute value read 153234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma * from the buffer without storing. So readImpl contains the common logic of the two, leaving 154234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma * the difference to a processUid function. 155234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma * 156234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma * @param processUid the callback function to process the uid entry in the buffer. 157234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma */ 158234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma private void readImpl(Consumer<IntBuffer> processUid) { 1592ab014426647bbc8960fdb4dadfe480b9806676eMike Ma synchronized (mProcReader) { 1602ab014426647bbc8960fdb4dadfe480b9806676eMike Ma ByteBuffer bytes = mProcReader.readBytes(); 1612ab014426647bbc8960fdb4dadfe480b9806676eMike Ma if (bytes == null || bytes.remaining() <= 4) { 1622ab014426647bbc8960fdb4dadfe480b9806676eMike Ma // Error already logged in mProcReader. 1632ab014426647bbc8960fdb4dadfe480b9806676eMike Ma return; 1642ab014426647bbc8960fdb4dadfe480b9806676eMike Ma } 1652ab014426647bbc8960fdb4dadfe480b9806676eMike Ma if ((bytes.remaining() & 3) != 0) { 1662ab014426647bbc8960fdb4dadfe480b9806676eMike Ma Slog.wtf(TAG, 1672ab014426647bbc8960fdb4dadfe480b9806676eMike Ma "Cannot parse cluster time proc bytes to int: " + bytes.remaining()); 1682ab014426647bbc8960fdb4dadfe480b9806676eMike Ma return; 1692ab014426647bbc8960fdb4dadfe480b9806676eMike Ma } 1702ab014426647bbc8960fdb4dadfe480b9806676eMike Ma IntBuffer buf = bytes.asIntBuffer(); 1712ab014426647bbc8960fdb4dadfe480b9806676eMike Ma final int numClusters = buf.get(); 1722ab014426647bbc8960fdb4dadfe480b9806676eMike Ma if (numClusters <= 0) { 1732ab014426647bbc8960fdb4dadfe480b9806676eMike Ma Slog.wtf(TAG, "Cluster time format error: " + numClusters); 1742ab014426647bbc8960fdb4dadfe480b9806676eMike Ma return; 1752ab014426647bbc8960fdb4dadfe480b9806676eMike Ma } 1762ab014426647bbc8960fdb4dadfe480b9806676eMike Ma if (mNumClusters == -1) { 1772ab014426647bbc8960fdb4dadfe480b9806676eMike Ma mNumClusters = numClusters; 1782ab014426647bbc8960fdb4dadfe480b9806676eMike Ma } 1792ab014426647bbc8960fdb4dadfe480b9806676eMike Ma if (buf.remaining() < numClusters) { 1802ab014426647bbc8960fdb4dadfe480b9806676eMike Ma Slog.wtf(TAG, "Too few data left in the buffer: " + buf.remaining()); 1813d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma return; 1823d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma } 1832ab014426647bbc8960fdb4dadfe480b9806676eMike Ma if (mNumCores <= 0) { 1842ab014426647bbc8960fdb4dadfe480b9806676eMike Ma if (!readCoreInfo(buf, numClusters)) { 1852ab014426647bbc8960fdb4dadfe480b9806676eMike Ma return; 1862ab014426647bbc8960fdb4dadfe480b9806676eMike Ma } 1872ab014426647bbc8960fdb4dadfe480b9806676eMike Ma } else { 1882ab014426647bbc8960fdb4dadfe480b9806676eMike Ma buf.position(buf.position() + numClusters); 1892ab014426647bbc8960fdb4dadfe480b9806676eMike Ma } 1903d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma 1912ab014426647bbc8960fdb4dadfe480b9806676eMike Ma if (buf.remaining() % (mNumCores + 1) != 0) { 1922ab014426647bbc8960fdb4dadfe480b9806676eMike Ma Slog.wtf(TAG, 1932ab014426647bbc8960fdb4dadfe480b9806676eMike Ma "Cluster time format error: " + buf.remaining() + " / " + (mNumCores 1942ab014426647bbc8960fdb4dadfe480b9806676eMike Ma + 1)); 1952ab014426647bbc8960fdb4dadfe480b9806676eMike Ma return; 1963d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma } 1972ab014426647bbc8960fdb4dadfe480b9806676eMike Ma int numUids = buf.remaining() / (mNumCores + 1); 1983d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma 1992ab014426647bbc8960fdb4dadfe480b9806676eMike Ma for (int i = 0; i < numUids; i++) { 200234d1828ca4a706676ad6f3fa0629959c74297c7Mike Ma processUid.accept(buf); 20169d8b3e0508de7e181f70a3322be626b1527ee0eMike Ma } 20269d8b3e0508de7e181f70a3322be626b1527ee0eMike Ma if (DEBUG) { 20369d8b3e0508de7e181f70a3322be626b1527ee0eMike Ma Slog.d(TAG, "Read uids: " + numUids); 2043d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma } 2053d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma } 2063d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma } 2073d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma 2082ab014426647bbc8960fdb4dadfe480b9806676eMike Ma // Returns if it has read valid info. 2092ab014426647bbc8960fdb4dadfe480b9806676eMike Ma private boolean readCoreInfo(IntBuffer buf, int numClusters) { 2102ab014426647bbc8960fdb4dadfe480b9806676eMike Ma int numCores = 0; 2112ab014426647bbc8960fdb4dadfe480b9806676eMike Ma int[] numCoresOnCluster = new int[numClusters]; 2122ab014426647bbc8960fdb4dadfe480b9806676eMike Ma for (int i = 0; i < numClusters; i++) { 2132ab014426647bbc8960fdb4dadfe480b9806676eMike Ma numCoresOnCluster[i] = buf.get(); 2142ab014426647bbc8960fdb4dadfe480b9806676eMike Ma numCores += numCoresOnCluster[i]; 2152ab014426647bbc8960fdb4dadfe480b9806676eMike Ma } 2162ab014426647bbc8960fdb4dadfe480b9806676eMike Ma if (numCores <= 0) { 2172ab014426647bbc8960fdb4dadfe480b9806676eMike Ma Slog.e(TAG, "Invalid # cores from cluster time proc file: " + numCores); 2182ab014426647bbc8960fdb4dadfe480b9806676eMike Ma return false; 2192ab014426647bbc8960fdb4dadfe480b9806676eMike Ma } 2202ab014426647bbc8960fdb4dadfe480b9806676eMike Ma mNumCores = numCores; 2212ab014426647bbc8960fdb4dadfe480b9806676eMike Ma mNumCoresOnCluster = numCoresOnCluster; 2222ab014426647bbc8960fdb4dadfe480b9806676eMike Ma mCurTime = new double[numClusters]; 2232ab014426647bbc8960fdb4dadfe480b9806676eMike Ma mDeltaTime = new long[numClusters]; 224ec67661bdfe1814039f625575176678219acc7b1Chenjie Yu mCurTimeRounded = new long[numClusters]; 2252ab014426647bbc8960fdb4dadfe480b9806676eMike Ma return true; 2262ab014426647bbc8960fdb4dadfe480b9806676eMike Ma } 2272ab014426647bbc8960fdb4dadfe480b9806676eMike Ma 2282ab014426647bbc8960fdb4dadfe480b9806676eMike Ma public void removeUid(int uid) { 2292ab014426647bbc8960fdb4dadfe480b9806676eMike Ma mLastUidPolicyTimeMs.delete(uid); 2302ab014426647bbc8960fdb4dadfe480b9806676eMike Ma } 2312ab014426647bbc8960fdb4dadfe480b9806676eMike Ma 2322ab014426647bbc8960fdb4dadfe480b9806676eMike Ma public void removeUidsInRange(int startUid, int endUid) { 2332ab014426647bbc8960fdb4dadfe480b9806676eMike Ma mLastUidPolicyTimeMs.put(startUid, null); 2342ab014426647bbc8960fdb4dadfe480b9806676eMike Ma mLastUidPolicyTimeMs.put(endUid, null); 2352ab014426647bbc8960fdb4dadfe480b9806676eMike Ma final int firstIndex = mLastUidPolicyTimeMs.indexOfKey(startUid); 2362ab014426647bbc8960fdb4dadfe480b9806676eMike Ma final int lastIndex = mLastUidPolicyTimeMs.indexOfKey(endUid); 2372ab014426647bbc8960fdb4dadfe480b9806676eMike Ma mLastUidPolicyTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1); 2382ab014426647bbc8960fdb4dadfe480b9806676eMike Ma } 2393d422c37b7f2caf364febc556e17b43c40c56be6Mike Ma} 240