Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b09540f3 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

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
parent 1b5a2a96
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -95,9 +95,13 @@ public class NetworkIdentity {

        final String subscriberId;
        if (isNetworkTypeMobile(type)) {
            if (state.subscriberId != null) {
                subscriberId = state.subscriberId;
            } else {
                final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
                        Context.TELEPHONY_SERVICE);
                subscriberId = telephony.getSubscriberId();
            }
        } else {
            subscriberId = null;
        }
+10 −0
Original line number Diff line number Diff line
@@ -29,18 +29,27 @@ public class NetworkState implements Parcelable {
    public final NetworkInfo networkInfo;
    public final LinkProperties linkProperties;
    public final LinkCapabilities linkCapabilities;
    /** Currently only used by testing. */
    public final String subscriberId;

    public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
            LinkCapabilities linkCapabilities) {
        this(networkInfo, linkProperties, linkCapabilities, null);
    }

    public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
            LinkCapabilities linkCapabilities, String subscriberId) {
        this.networkInfo = networkInfo;
        this.linkProperties = linkProperties;
        this.linkCapabilities = linkCapabilities;
        this.subscriberId = subscriberId;
    }

    public NetworkState(Parcel in) {
        networkInfo = in.readParcelable(null);
        linkProperties = in.readParcelable(null);
        linkCapabilities = in.readParcelable(null);
        subscriberId = in.readString();
    }

    /** {@inheritDoc} */
@@ -53,6 +62,7 @@ public class NetworkState implements Parcelable {
        out.writeParcelable(networkInfo, flags);
        out.writeParcelable(linkProperties, flags);
        out.writeParcelable(linkCapabilities, flags);
        out.writeString(subscriberId);
    }

    public static final Creator<NetworkState> CREATOR = new Creator<NetworkState>() {
+8 −0
Original line number Diff line number Diff line
@@ -41,6 +41,14 @@ public class TrafficStats {
     */
    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
     * session started, or {@code null} if no session active.
+24 −4
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import static android.Manifest.permission.MANAGE_APP_TOKENS;
import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.READ_PHONE_STATE;
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.TYPE_MOBILE;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
@@ -247,9 +249,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
        mContext.registerReceiver(mScreenReceiver, screenFilter);

        // watch for network interfaces to be claimed
        final IntentFilter ifaceFilter = new IntentFilter();
        ifaceFilter.addAction(CONNECTIVITY_ACTION);
        mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler);
        final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION);
        mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler);

        // listen for uid removal to clean policy
        final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED);
        mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler);

        // listen for warning polling events; currently dispatched by
        final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED);
@@ -312,6 +317,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.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 (mRulesLock) {
                // remove any policy and update rules to clean up
                mUidPolicy.delete(uid);
                updateRulesForUidLocked(uid);
                writePolicyLocked();
            }
        }
    };

    /**
     * Receiver that watches for {@link INetworkStatsService} updates, which we
     * use to check against {@link NetworkPolicy#warningBytes}.
@@ -473,7 +493,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
     * Receiver that watches for {@link IConnectivityManager} to claim network
     * interfaces. Used to apply {@link NetworkPolicy} to matching networks.
     */
    private BroadcastReceiver mIfaceReceiver = new BroadcastReceiver() {
    private BroadcastReceiver mConnReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // on background handler thread, and verified CONNECTIVITY_INTERNAL
+60 −9
Original line number Diff line number Diff line
@@ -19,11 +19,14 @@ package com.android.server.net;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.DUMP;
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.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.TAG_NONE;
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_MAX_HISTORY;
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
        final IntentFilter ifaceFilter = new IntentFilter();
        ifaceFilter.addAction(CONNECTIVITY_ACTION);
        mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler);
        final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION);
        mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler);

        // listen for periodic polling events
        final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
        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
        final IntentFilter shutdownFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
        mContext.registerReceiver(mShutdownReceiver, shutdownFilter, SHUTDOWN, null);
        final IntentFilter shutdownFilter = new IntentFilter(ACTION_SHUTDOWN);
        mContext.registerReceiver(mShutdownReceiver, shutdownFilter);

        try {
            registerPollAlarmLocked();
@@ -226,8 +232,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
    }

    private void shutdownLocked() {
        mContext.unregisterReceiver(mIfaceReceiver);
        mContext.unregisterReceiver(mConnReceiver);
        mContext.unregisterReceiver(mPollReceiver);
        mContext.unregisterReceiver(mRemovedReceiver);
        mContext.unregisterReceiver(mShutdownReceiver);

        writeNetworkStatsLocked();
@@ -352,7 +359,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
     * interfaces. Used to associate {@link TelephonyManager#getSubscriberId()}
     * with mobile interfaces.
     */
    private BroadcastReceiver mIfaceReceiver = new BroadcastReceiver() {
    private BroadcastReceiver mConnReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // 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() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // verified SHUTDOWN permission above.
            // SHUTDOWN is protected broadcast.
            synchronized (mStatsLock) {
                shutdownLocked();
            }
@@ -545,6 +564,31 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
        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) {
        final long bucketDuration = mSettings.getNetworkBucketDuration();
        final NetworkStatsHistory existing = mNetworkStats.get(ident);
@@ -568,6 +612,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
    }

    private NetworkStatsHistory findOrCreateUidStatsLocked(NetworkIdentitySet ident, int uid) {
        ensureUidStatsLoadedLocked();

        // find bucket for identity first, then find uid
        SparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident);
        if (uidStats == null) {
@@ -734,6 +780,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
    private void 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

        FileOutputStream fos = null;
Loading