import android.os.SystemClock;
import android.util.SparseBooleanArray;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Objects;
import java.io.CharArrayWriter;
* Return all rows except those attributed to the requested UID; doesn't
* mutate the original structure.
*/
- public NetworkStats withoutUid(int uid) {
+ public NetworkStats withoutUids(int[] uids) {
final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
Entry entry = new Entry();
for (int i = 0; i < size; i++) {
entry = getValues(i, entry);
- if (entry.uid != uid) {
+ if (!ArrayUtils.contains(uids, entry.uid)) {
stats.addValues(entry);
}
}
.addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L)
.addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 128L, 8L, 0L, 0L, 0L);
- final NetworkStats after = before.withoutUid(100);
+ final NetworkStats after = before.withoutUids(new int[] { 100 });
assertEquals(6, before.size());
assertEquals(2, after.size());
assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
import android.text.format.DateUtils;
import android.util.AtomicFile;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Objects;
* moving any {@link NetworkStats#TAG_NONE} series to
* {@link TrafficStats#UID_REMOVED}.
*/
- public void removeUid(int uid) {
+ public void removeUids(int[] uids) {
final ArrayList<Key> knownKeys = Lists.newArrayList();
knownKeys.addAll(mStats.keySet());
// migrate all UID stats into special "removed" bucket
for (Key key : knownKeys) {
- if (key.uid == uid) {
+ if (ArrayUtils.contains(uids, key.uid)) {
// only migrate combined TAG_NONE history
if (key.tag == TAG_NONE) {
final NetworkStatsHistory uidHistory = mStats.get(key);
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
* Remove the given UID from all {@link FileRotator} history, migrating it
* to {@link TrafficStats#UID_REMOVED}.
*/
- public void removeUidLocked(int uid) {
+ public void removeUidsLocked(int[] uids) {
try {
- // process all existing data to migrate uid
- mRotator.rewriteAll(new RemoveUidRewriter(mBucketDuration, uid));
+ // Rewrite all persisted data to migrate UID stats
+ mRotator.rewriteAll(new RemoveUidRewriter(mBucketDuration, uids));
} catch (IOException e) {
- Log.wtf(TAG, "problem removing UID " + uid, e);
+ Log.wtf(TAG, "problem removing UIDs " + Arrays.toString(uids), e);
recoverFromWtf();
}
- // clear UID from current stats snapshot
+ // Remove any pending stats
+ mPending.removeUids(uids);
+ mSinceBoot.removeUids(uids);
+
+ // Clear UID from current stats snapshot
if (mLastSnapshot != null) {
- mLastSnapshot = mLastSnapshot.withoutUid(uid);
+ mLastSnapshot = mLastSnapshot.withoutUids(uids);
}
final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
if (complete != null) {
- complete.removeUid(uid);
+ complete.removeUids(uids);
}
}
*/
public static class RemoveUidRewriter implements FileRotator.Rewriter {
private final NetworkStatsCollection mTemp;
- private final int mUid;
+ private final int[] mUids;
- public RemoveUidRewriter(long bucketDuration, int uid) {
+ public RemoveUidRewriter(long bucketDuration, int[] uids) {
mTemp = new NetworkStatsCollection(bucketDuration);
- mUid = uid;
+ mUids = uids;
}
@Override
public void read(InputStream in) throws IOException {
mTemp.read(in);
mTemp.clearDirty();
- mTemp.removeUid(mUid);
+ mTemp.removeUids(mUids);
}
@Override
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.content.Intent.ACTION_SHUTDOWN;
import static android.content.Intent.ACTION_UID_REMOVED;
+import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkStatsService;
import android.util.SparseIntArray;
import android.util.TrustedTime;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
/**
* Collect and persist detailed network statistics, and provide this data to
final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED);
mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler);
+ // listen for user changes to clean stats
+ final IntentFilter userFilter = new IntentFilter(ACTION_USER_REMOVED);
+ mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
+
// persist stats during clean shutdown
final IntentFilter shutdownFilter = new IntentFilter(ACTION_SHUTDOWN);
mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
public void onReceive(Context context, Intent intent) {
// on background handler thread, and UID_REMOVED is protected
// broadcast.
- final int uid = intent.getIntExtra(EXTRA_UID, 0);
+
+ final int uid = intent.getIntExtra(EXTRA_UID, -1);
+ if (uid == -1) return;
+
+ synchronized (mStatsLock) {
+ mWakeLock.acquire();
+ try {
+ removeUidsLocked(uid);
+ } finally {
+ mWakeLock.release();
+ }
+ }
+ }
+ };
+
+ private BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // On background handler thread, and USER_REMOVED is protected
+ // broadcast.
+
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userId == -1) return;
+
synchronized (mStatsLock) {
mWakeLock.acquire();
try {
- removeUidLocked(uid);
+ removeUserLocked(userId);
} finally {
mWakeLock.release();
}
/**
* Clean up {@link #mUidRecorder} after UID is removed.
*/
- private void removeUidLocked(int uid) {
- // perform one last poll before removing
+ private void removeUidsLocked(int... uids) {
+ if (LOGV) Slog.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
+
+ // Perform one last poll before removing
performPollLocked(FLAG_PERSIST_ALL);
- mUidRecorder.removeUidLocked(uid);
- mUidTagRecorder.removeUidLocked(uid);
+ mUidRecorder.removeUidsLocked(uids);
+ mUidTagRecorder.removeUidsLocked(uids);
+
+ // Clear kernel stats associated with UID
+ for (int uid : uids) {
+ resetKernelUidStats(uid);
+ }
+ }
+
+ /**
+ * Clean up {@link #mUidRecorder} after user is removed.
+ */
+ private void removeUserLocked(int userId) {
+ if (LOGV) Slog.v(TAG, "removeUserLocked() for userId=" + userId);
+
+ // Build list of UIDs that we should clean up
+ int[] uids = new int[0];
+ final List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
+ PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_DISABLED_COMPONENTS);
+ for (ApplicationInfo app : apps) {
+ final int uid = UserHandle.getUid(userId, app.uid);
+ uids = ArrayUtils.appendInt(uids, uid);
+ }
- // clear kernel stats associated with UID
- resetKernelUidStats(uid);
+ removeUidsLocked(uids);
}
@Override