NetworkPolicyManagerService.java revision 1b861278a2051f53ce7955fb7992fa536dc975d9
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.net;
18
19import static android.Manifest.permission.DUMP;
20import static android.Manifest.permission.MANAGE_APP_TOKENS;
21import static android.Manifest.permission.UPDATE_DEVICE_STATS;
22import static android.net.NetworkPolicyManager.POLICY_NONE;
23import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND;
24import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
25import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
26import static android.net.NetworkPolicyManager.dumpPolicy;
27import static android.net.NetworkPolicyManager.dumpRules;
28
29import android.app.IActivityManager;
30import android.app.IProcessObserver;
31import android.content.BroadcastReceiver;
32import android.content.Context;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.net.ConnectivityManager;
36import android.net.INetworkPolicyListener;
37import android.net.INetworkPolicyManager;
38import android.os.IPowerManager;
39import android.os.RemoteCallbackList;
40import android.os.RemoteException;
41import android.util.Log;
42import android.util.Slog;
43import android.util.SparseArray;
44import android.util.SparseBooleanArray;
45import android.util.SparseIntArray;
46
47import java.io.FileDescriptor;
48import java.io.PrintWriter;
49
50/**
51 * Service that maintains low-level network policy rules and collects usage
52 * statistics to drive those rules.
53 * <p>
54 * Derives active rules by combining a given policy with other system status,
55 * and delivers to listeners, such as {@link ConnectivityManager}, for
56 * enforcement.
57 */
58public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
59    private static final String TAG = "NetworkPolicy";
60    private static final boolean LOGD = true;
61
62    private Context mContext;
63    private IActivityManager mActivityManager;
64    private IPowerManager mPowerManager;
65
66    private Object mRulesLock = new Object();
67
68    private boolean mScreenOn;
69
70    /** Current network policy for each UID. */
71    private SparseIntArray mUidPolicy = new SparseIntArray();
72    /** Current derived network rules for each UID. */
73    private SparseIntArray mUidRules = new SparseIntArray();
74
75    /** Foreground at both UID and PID granularity. */
76    private SparseBooleanArray mUidForeground = new SparseBooleanArray();
77    private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
78            SparseBooleanArray>();
79
80    private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<
81            INetworkPolicyListener>();
82
83    // TODO: periodically poll network stats and write to disk
84    // TODO: save/restore policy information from disk
85
86    // TODO: keep whitelist of system-critical services that should never have
87    // rules enforced, such as system, phone, and radio UIDs.
88
89    public NetworkPolicyManagerService(
90            Context context, IActivityManager activityManager, IPowerManager powerManager) {
91        mContext = checkNotNull(context, "missing context");
92        mActivityManager = checkNotNull(activityManager, "missing activityManager");
93        mPowerManager = checkNotNull(powerManager, "missing powerManager");
94    }
95
96    public void systemReady() {
97        // TODO: read current policy+stats from disk and generate NMS rules
98
99        updateScreenOn();
100
101        try {
102            mActivityManager.registerProcessObserver(mProcessObserver);
103        } catch (RemoteException e) {
104            // ouch, no foregroundActivities updates means some processes may
105            // never get network access.
106            Slog.e(TAG, "unable to register IProcessObserver", e);
107        }
108
109        // TODO: traverse existing processes to know foreground state, or have
110        // activitymanager dispatch current state when new observer attached.
111
112        final IntentFilter screenFilter = new IntentFilter();
113        screenFilter.addAction(Intent.ACTION_SCREEN_ON);
114        screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
115        mContext.registerReceiver(mScreenReceiver, screenFilter);
116
117        final IntentFilter shutdownFilter = new IntentFilter();
118        shutdownFilter.addAction(Intent.ACTION_SHUTDOWN);
119        mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
120
121    }
122
123    private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
124        @Override
125        public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
126            // only someone like AMS should only be calling us
127            mContext.enforceCallingOrSelfPermission(
128                    MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission");
129
130            synchronized (mRulesLock) {
131                // because a uid can have multiple pids running inside, we need to
132                // remember all pid states and summarize foreground at uid level.
133
134                // record foreground for this specific pid
135                SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
136                if (pidForeground == null) {
137                    pidForeground = new SparseBooleanArray(2);
138                    mUidPidForeground.put(uid, pidForeground);
139                }
140                pidForeground.put(pid, foregroundActivities);
141                computeUidForegroundL(uid);
142            }
143        }
144
145        @Override
146        public void onProcessDied(int pid, int uid) {
147            // only someone like AMS should only be calling us
148            mContext.enforceCallingOrSelfPermission(
149                    MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission");
150
151            synchronized (mRulesLock) {
152                // clear records and recompute, when they exist
153                final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
154                if (pidForeground != null) {
155                    pidForeground.delete(pid);
156                    computeUidForegroundL(uid);
157                }
158            }
159        }
160    };
161
162    private BroadcastReceiver mScreenReceiver = new BroadcastReceiver() {
163        @Override
164        public void onReceive(Context context, Intent intent) {
165            synchronized (mRulesLock) {
166                // screen-related broadcasts are protected by system, no need
167                // for permissions check.
168                updateScreenOn();
169            }
170        }
171    };
172
173    private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
174        @Override
175        public void onReceive(Context context, Intent intent) {
176            // TODO: persist any pending stats during clean shutdown
177            Log.d(TAG, "persisting stats");
178        }
179    };
180
181    @Override
182    public void setUidPolicy(int uid, int policy) {
183        // TODO: create permission for modifying data policy
184        mContext.enforceCallingOrSelfPermission(
185                UPDATE_DEVICE_STATS, "requires UPDATE_DEVICE_STATS permission");
186
187        final int oldPolicy;
188        synchronized (mRulesLock) {
189            oldPolicy = getUidPolicy(uid);
190            mUidPolicy.put(uid, policy);
191        }
192
193        // TODO: consider dispatching BACKGROUND_DATA_SETTING broadcast
194    }
195
196    @Override
197    public int getUidPolicy(int uid) {
198        synchronized (mRulesLock) {
199            return mUidPolicy.get(uid, POLICY_NONE);
200        }
201    }
202
203    @Override
204    public void registerListener(INetworkPolicyListener listener) {
205        mListeners.register(listener);
206
207        synchronized (mRulesLock) {
208            // dispatch any existing rules to new listeners
209            final int size = mUidRules.size();
210            for (int i = 0; i < size; i++) {
211                final int uid = mUidRules.keyAt(i);
212                final int uidRules = mUidRules.valueAt(i);
213                if (uidRules != RULE_ALLOW_ALL) {
214                    try {
215                        listener.onRulesChanged(uid, uidRules);
216                    } catch (RemoteException e) {
217                    }
218                }
219            }
220        }
221    }
222
223    @Override
224    public void unregisterListener(INetworkPolicyListener listener) {
225        mListeners.unregister(listener);
226    }
227
228    @Override
229    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
230        mContext.enforceCallingOrSelfPermission(DUMP, "requires DUMP permission");
231
232        synchronized (mRulesLock) {
233            fout.println("Policy status for known UIDs:");
234
235            final SparseBooleanArray knownUids = new SparseBooleanArray();
236            collectKeys(mUidPolicy, knownUids);
237            collectKeys(mUidForeground, knownUids);
238            collectKeys(mUidRules, knownUids);
239
240            final int size = knownUids.size();
241            for (int i = 0; i < size; i++) {
242                final int uid = knownUids.keyAt(i);
243                fout.print("  UID=");
244                fout.print(uid);
245
246                fout.print(" policy=");
247                final int policyIndex = mUidPolicy.indexOfKey(uid);
248                if (policyIndex < 0) {
249                    fout.print("UNKNOWN");
250                } else {
251                    dumpPolicy(fout, mUidPolicy.valueAt(policyIndex));
252                }
253
254                fout.print(" foreground=");
255                final int foregroundIndex = mUidPidForeground.indexOfKey(uid);
256                if (foregroundIndex < 0) {
257                    fout.print("UNKNOWN");
258                } else {
259                    dumpSparseBooleanArray(fout, mUidPidForeground.valueAt(foregroundIndex));
260                }
261
262                fout.print(" rules=");
263                final int rulesIndex = mUidRules.indexOfKey(uid);
264                if (rulesIndex < 0) {
265                    fout.print("UNKNOWN");
266                } else {
267                    dumpRules(fout, mUidRules.valueAt(rulesIndex));
268                }
269
270                fout.println();
271            }
272        }
273    }
274
275    private boolean isUidForegroundL(int uid) {
276        // only really in foreground when screen is also on
277        return mUidForeground.get(uid, false) && mScreenOn;
278    }
279
280    /**
281     * Foreground for PID changed; recompute foreground at UID level. If
282     * changed, will trigger {@link #updateRulesForUidL(int)}.
283     */
284    private void computeUidForegroundL(int uid) {
285        final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
286
287        // current pid is dropping foreground; examine other pids
288        boolean uidForeground = false;
289        final int size = pidForeground.size();
290        for (int i = 0; i < size; i++) {
291            if (pidForeground.valueAt(i)) {
292                uidForeground = true;
293                break;
294            }
295        }
296
297        final boolean oldUidForeground = mUidForeground.get(uid, false);
298        if (oldUidForeground != uidForeground) {
299            // foreground changed, push updated rules
300            mUidForeground.put(uid, uidForeground);
301            updateRulesForUidL(uid);
302        }
303    }
304
305    private void updateScreenOn() {
306        synchronized (mRulesLock) {
307            try {
308                mScreenOn = mPowerManager.isScreenOn();
309            } catch (RemoteException e) {
310            }
311            updateRulesForScreenL();
312        }
313    }
314
315    /**
316     * Update rules that might be changed by {@link #mScreenOn} value.
317     */
318    private void updateRulesForScreenL() {
319        // only update rules for anyone with foreground activities
320        final int size = mUidForeground.size();
321        for (int i = 0; i < size; i++) {
322            if (mUidForeground.valueAt(i)) {
323                final int uid = mUidForeground.keyAt(i);
324                updateRulesForUidL(uid);
325            }
326        }
327    }
328
329    private void updateRulesForUidL(int uid) {
330        final int uidPolicy = getUidPolicy(uid);
331        final boolean uidForeground = isUidForegroundL(uid);
332
333        // derive active rules based on policy and active state
334        int uidRules = RULE_ALLOW_ALL;
335        if (!uidForeground && (uidPolicy & POLICY_REJECT_PAID_BACKGROUND) != 0) {
336            // uid in background, and policy says to block paid data
337            uidRules = RULE_REJECT_PAID;
338        }
339
340        // TODO: only dispatch when rules actually change
341
342        // record rule locally to dispatch to new listeners
343        mUidRules.put(uid, uidRules);
344
345        // dispatch changed rule to existing listeners
346        final int length = mListeners.beginBroadcast();
347        for (int i = 0; i < length; i++) {
348            final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
349            if (listener != null) {
350                try {
351                    listener.onRulesChanged(uid, uidRules);
352                } catch (RemoteException e) {
353                }
354            }
355        }
356        mListeners.finishBroadcast();
357    }
358
359    private static <T> T checkNotNull(T value, String message) {
360        if (value == null) {
361            throw new NullPointerException(message);
362        }
363        return value;
364    }
365
366    private static void collectKeys(SparseIntArray source, SparseBooleanArray target) {
367        final int size = source.size();
368        for (int i = 0; i < size; i++) {
369            target.put(source.keyAt(i), true);
370        }
371    }
372
373    private static void collectKeys(SparseBooleanArray source, SparseBooleanArray target) {
374        final int size = source.size();
375        for (int i = 0; i < size; i++) {
376            target.put(source.keyAt(i), true);
377        }
378    }
379
380    private static void dumpSparseBooleanArray(PrintWriter fout, SparseBooleanArray value) {
381        fout.print("[");
382        final int size = value.size();
383        for (int i = 0; i < size; i++) {
384            fout.print(value.keyAt(i) + "=" + value.valueAt(i));
385            if (i < size - 1) fout.print(",");
386        }
387        fout.print("]");
388    }
389}
390