Handle removed UIDs in network stats and policy.

When UID_REMOVED, clean up any existing UID network policy so it
doesn't linger for future apps.  Also move any NetworkStatsHistory
to special UID_REMOVED tracking bucket.

Tests for new removal code.  Also test detailed UID stats, including
network changes to verify template matching logic.

Bug: 4584212
Change-Id: I9faadf6b6f3830eb45d86c7f1980a27cdbcdb11e
This commit is contained in:
Jeff Sharkey
2011-06-19 01:08:12 -07:00
parent 0d5baa3f21
commit c04cda602c
3 changed files with 75 additions and 12 deletions

View File

@@ -95,9 +95,13 @@ public class NetworkIdentity {
final String subscriberId; final String subscriberId;
if (isNetworkTypeMobile(type)) { if (isNetworkTypeMobile(type)) {
final TelephonyManager telephony = (TelephonyManager) context.getSystemService( if (state.subscriberId != null) {
Context.TELEPHONY_SERVICE); subscriberId = state.subscriberId;
subscriberId = telephony.getSubscriberId(); } else {
final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
Context.TELEPHONY_SERVICE);
subscriberId = telephony.getSubscriberId();
}
} else { } else {
subscriberId = null; subscriberId = null;
} }

View File

@@ -41,6 +41,14 @@ public class TrafficStats {
*/ */
public final static int UNSUPPORTED = -1; public final static int UNSUPPORTED = -1;
/**
* Special UID value used when collecting {@link NetworkStatsHistory} for
* removed applications.
*
* @hide
*/
public static final int UID_REMOVED = -4;
/** /**
* Snapshot of {@link NetworkStats} when the currently active profiling * Snapshot of {@link NetworkStats} when the currently active profiling
* session started, or {@code null} if no session active. * session started, or {@code null} if no session active.

View File

@@ -19,11 +19,14 @@ package com.android.server.net;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.DUMP; import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.SHUTDOWN; import static android.content.Intent.ACTION_SHUTDOWN;
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_REMOVED;
import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION; import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION;
import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY; import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY;
import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD; import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
@@ -206,17 +209,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
// watch for network interfaces to be claimed // watch for network interfaces to be claimed
final IntentFilter ifaceFilter = new IntentFilter(); final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION);
ifaceFilter.addAction(CONNECTIVITY_ACTION); mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler);
mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler);
// listen for periodic polling events // listen for periodic polling events
final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL); final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
mContext.registerReceiver(mPollReceiver, pollFilter, READ_NETWORK_USAGE_HISTORY, mHandler); mContext.registerReceiver(mPollReceiver, pollFilter, READ_NETWORK_USAGE_HISTORY, mHandler);
// listen for uid removal to clean stats
final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED);
mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler);
// persist stats during clean shutdown // persist stats during clean shutdown
final IntentFilter shutdownFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); final IntentFilter shutdownFilter = new IntentFilter(ACTION_SHUTDOWN);
mContext.registerReceiver(mShutdownReceiver, shutdownFilter, SHUTDOWN, null); mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
try { try {
registerPollAlarmLocked(); registerPollAlarmLocked();
@@ -226,8 +232,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
private void shutdownLocked() { private void shutdownLocked() {
mContext.unregisterReceiver(mIfaceReceiver); mContext.unregisterReceiver(mConnReceiver);
mContext.unregisterReceiver(mPollReceiver); mContext.unregisterReceiver(mPollReceiver);
mContext.unregisterReceiver(mRemovedReceiver);
mContext.unregisterReceiver(mShutdownReceiver); mContext.unregisterReceiver(mShutdownReceiver);
writeNetworkStatsLocked(); writeNetworkStatsLocked();
@@ -352,7 +359,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
* interfaces. Used to associate {@link TelephonyManager#getSubscriberId()} * interfaces. Used to associate {@link TelephonyManager#getSubscriberId()}
* with mobile interfaces. * with mobile interfaces.
*/ */
private BroadcastReceiver mIfaceReceiver = new BroadcastReceiver() { private BroadcastReceiver mConnReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
// on background handler thread, and verified CONNECTIVITY_INTERNAL // on background handler thread, and verified CONNECTIVITY_INTERNAL
@@ -375,10 +382,22 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
}; };
private BroadcastReceiver mRemovedReceiver = new BroadcastReceiver() {
@Override
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);
synchronized (mStatsLock) {
removeUidLocked(uid);
}
}
};
private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() { private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
// verified SHUTDOWN permission above. // SHUTDOWN is protected broadcast.
synchronized (mStatsLock) { synchronized (mStatsLock) {
shutdownLocked(); shutdownLocked();
} }
@@ -545,6 +564,31 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mLastUidPoll = uidStats; mLastUidPoll = uidStats;
} }
/**
* Clean up {@link #mUidStats} after UID is removed.
*/
private void removeUidLocked(int uid) {
ensureUidStatsLoadedLocked();
// migrate all UID stats into special "removed" bucket
for (NetworkIdentitySet ident : mUidStats.keySet()) {
final SparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident);
final NetworkStatsHistory uidHistory = uidStats.get(uid);
if (uidHistory != null) {
final NetworkStatsHistory removedHistory = findOrCreateUidStatsLocked(
ident, UID_REMOVED);
removedHistory.recordEntireHistory(uidHistory);
uidStats.remove(uid);
}
}
// TODO: push kernel event to wipe stats for UID, otherwise we risk
// picking them up again during next poll.
// since this was radical rewrite, push to disk
writeUidStatsLocked();
}
private NetworkStatsHistory findOrCreateNetworkStatsLocked(NetworkIdentitySet ident) { private NetworkStatsHistory findOrCreateNetworkStatsLocked(NetworkIdentitySet ident) {
final long bucketDuration = mSettings.getNetworkBucketDuration(); final long bucketDuration = mSettings.getNetworkBucketDuration();
final NetworkStatsHistory existing = mNetworkStats.get(ident); final NetworkStatsHistory existing = mNetworkStats.get(ident);
@@ -568,6 +612,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
private NetworkStatsHistory findOrCreateUidStatsLocked(NetworkIdentitySet ident, int uid) { private NetworkStatsHistory findOrCreateUidStatsLocked(NetworkIdentitySet ident, int uid) {
ensureUidStatsLoadedLocked();
// find bucket for identity first, then find uid // find bucket for identity first, then find uid
SparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident); SparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident);
if (uidStats == null) { if (uidStats == null) {
@@ -734,6 +780,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private void writeUidStatsLocked() { private void writeUidStatsLocked() {
if (LOGV) Slog.v(TAG, "writeUidStatsLocked()"); if (LOGV) Slog.v(TAG, "writeUidStatsLocked()");
if (!mUidStatsLoaded) {
Slog.w(TAG, "asked to write UID stats when not loaded; skipping");
return;
}
// TODO: consider duplicating stats and releasing lock while writing // TODO: consider duplicating stats and releasing lock while writing
FileOutputStream fos = null; FileOutputStream fos = null;