blob: c8f617e65df995260b71bc1670253a4d791a4042 [file] [log] [blame]
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -07001/*
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
Jeff Sharkey21c9c452011-06-07 12:26:43 -070019import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
Jeff Sharkey1b861272011-05-22 00:34:52 -070020import static android.Manifest.permission.DUMP;
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -070021import static android.Manifest.permission.MANAGE_APP_TOKENS;
Jeff Sharkey21c9c452011-06-07 12:26:43 -070022import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
23import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -070024import static android.net.NetworkPolicyManager.POLICY_NONE;
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -070025import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND;
26import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
27import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
Jeff Sharkey1b861272011-05-22 00:34:52 -070028import static android.net.NetworkPolicyManager.dumpPolicy;
29import static android.net.NetworkPolicyManager.dumpRules;
Jeff Sharkey21c9c452011-06-07 12:26:43 -070030import static android.text.format.DateUtils.DAY_IN_MILLIS;
31import static android.text.format.Time.MONTH_DAY;
32import static com.android.internal.util.Preconditions.checkNotNull;
33import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
34import static org.xmlpull.v1.XmlPullParser.START_TAG;
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -070035
Jeff Sharkeya4620792011-05-20 15:29:23 -070036import android.app.IActivityManager;
37import android.app.IProcessObserver;
38import android.content.BroadcastReceiver;
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -070039import android.content.Context;
Jeff Sharkeya4620792011-05-20 15:29:23 -070040import android.content.Intent;
41import android.content.IntentFilter;
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -070042import android.net.ConnectivityManager;
Jeff Sharkey21c9c452011-06-07 12:26:43 -070043import android.net.IConnectivityManager;
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -070044import android.net.INetworkPolicyListener;
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -070045import android.net.INetworkPolicyManager;
Jeff Sharkey75279902011-05-24 18:39:45 -070046import android.net.INetworkStatsService;
Jeff Sharkey21c9c452011-06-07 12:26:43 -070047import android.net.NetworkPolicy;
48import android.net.NetworkState;
49import android.net.NetworkStats;
50import android.os.Environment;
51import android.os.Handler;
52import android.os.HandlerThread;
Jeff Sharkeya4620792011-05-20 15:29:23 -070053import android.os.IPowerManager;
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -070054import android.os.RemoteCallbackList;
Jeff Sharkeya4620792011-05-20 15:29:23 -070055import android.os.RemoteException;
Jeff Sharkey21c9c452011-06-07 12:26:43 -070056import android.text.format.Time;
57import android.util.NtpTrustedTime;
Jeff Sharkeya4620792011-05-20 15:29:23 -070058import android.util.Slog;
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -070059import android.util.SparseArray;
60import android.util.SparseBooleanArray;
61import android.util.SparseIntArray;
Jeff Sharkey21c9c452011-06-07 12:26:43 -070062import android.util.TrustedTime;
63import android.util.Xml;
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -070064
Jeff Sharkey21c9c452011-06-07 12:26:43 -070065import com.android.internal.os.AtomicFile;
66import com.android.internal.util.FastXmlSerializer;
67import com.android.internal.util.Objects;
68import com.google.android.collect.Lists;
69import com.google.android.collect.Maps;
70
71import org.xmlpull.v1.XmlPullParser;
72import org.xmlpull.v1.XmlPullParserException;
73import org.xmlpull.v1.XmlSerializer;
74
75import java.io.File;
Jeff Sharkey1b861272011-05-22 00:34:52 -070076import java.io.FileDescriptor;
Jeff Sharkey21c9c452011-06-07 12:26:43 -070077import java.io.FileInputStream;
78import java.io.FileNotFoundException;
79import java.io.FileOutputStream;
80import java.io.IOException;
Jeff Sharkey1b861272011-05-22 00:34:52 -070081import java.io.PrintWriter;
Jeff Sharkey21c9c452011-06-07 12:26:43 -070082import java.net.ProtocolException;
83import java.util.ArrayList;
84import java.util.Arrays;
85import java.util.HashMap;
86
87import libcore.io.IoUtils;
Jeff Sharkey1b861272011-05-22 00:34:52 -070088
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -070089/**
90 * Service that maintains low-level network policy rules and collects usage
91 * statistics to drive those rules.
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -070092 * <p>
93 * Derives active rules by combining a given policy with other system status,
94 * and delivers to listeners, such as {@link ConnectivityManager}, for
95 * enforcement.
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -070096 */
97public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
98 private static final String TAG = "NetworkPolicy";
99 private static final boolean LOGD = true;
100
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700101 private static final int VERSION_CURRENT = 1;
102
103 private static final String TAG_POLICY_LIST = "policy-list";
104 private static final String TAG_NETWORK_POLICY = "network-policy";
105 private static final String TAG_UID_POLICY = "uid-policy";
106
107 private static final String ATTR_VERSION = "version";
108 private static final String ATTR_NETWORK_TEMPLATE = "networkTemplate";
109 private static final String ATTR_SUBSCRIBER_ID = "subscriberId";
110 private static final String ATTR_CYCLE_DAY = "cycleDay";
111 private static final String ATTR_WARNING_BYTES = "warningBytes";
112 private static final String ATTR_LIMIT_BYTES = "limitBytes";
113 private static final String ATTR_UID = "uid";
114 private static final String ATTR_POLICY = "policy";
115
116 private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
117
Jeff Sharkey75279902011-05-24 18:39:45 -0700118 private final Context mContext;
119 private final IActivityManager mActivityManager;
120 private final IPowerManager mPowerManager;
121 private final INetworkStatsService mNetworkStats;
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700122 private final TrustedTime mTime;
123
124 private IConnectivityManager mConnManager;
Jeff Sharkeya4620792011-05-20 15:29:23 -0700125
Jeff Sharkey75279902011-05-24 18:39:45 -0700126 private final Object mRulesLock = new Object();
Jeff Sharkeya4620792011-05-20 15:29:23 -0700127
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -0700128 private boolean mScreenOn;
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700129
130 /** Current network policy for each UID. */
Jeff Sharkeya4620792011-05-20 15:29:23 -0700131 private SparseIntArray mUidPolicy = new SparseIntArray();
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -0700132 /** Current derived network rules for each UID. */
133 private SparseIntArray mUidRules = new SparseIntArray();
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700134
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700135 /** Set of policies for strong network templates. */
136 private HashMap<StrongTemplate, NetworkPolicy> mTemplatePolicy = Maps.newHashMap();
137
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700138 /** Foreground at both UID and PID granularity. */
Jeff Sharkeya4620792011-05-20 15:29:23 -0700139 private SparseBooleanArray mUidForeground = new SparseBooleanArray();
140 private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
141 SparseBooleanArray>();
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700142
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -0700143 private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<
144 INetworkPolicyListener>();
145
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700146 private final HandlerThread mHandlerThread;
147 private final Handler mHandler;
148
149 private final AtomicFile mPolicyFile;
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700150
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -0700151 // TODO: keep whitelist of system-critical services that should never have
152 // rules enforced, such as system, phone, and radio UIDs.
153
Jeff Sharkeyd2a45872011-05-28 20:56:34 -0700154 // TODO: dispatch callbacks through handler when locked
155
Jeff Sharkey75279902011-05-24 18:39:45 -0700156 public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
157 IPowerManager powerManager, INetworkStatsService networkStats) {
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700158 // TODO: move to using cached NtpTrustedTime
159 this(context, activityManager, powerManager, networkStats, new NtpTrustedTime(),
160 getSystemDir());
161 }
162
163 private static File getSystemDir() {
164 return new File(Environment.getDataDirectory(), "system");
165 }
166
167 public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
168 IPowerManager powerManager, INetworkStatsService networkStats, TrustedTime time,
169 File systemDir) {
Jeff Sharkeya4620792011-05-20 15:29:23 -0700170 mContext = checkNotNull(context, "missing context");
171 mActivityManager = checkNotNull(activityManager, "missing activityManager");
172 mPowerManager = checkNotNull(powerManager, "missing powerManager");
Jeff Sharkey75279902011-05-24 18:39:45 -0700173 mNetworkStats = checkNotNull(networkStats, "missing networkStats");
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700174 mTime = checkNotNull(time, "missing TrustedTime");
175
176 mHandlerThread = new HandlerThread(TAG);
177 mHandlerThread.start();
178 mHandler = new Handler(mHandlerThread.getLooper());
179
180 mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"));
181 }
182
183 public void bindConnectivityManager(IConnectivityManager connManager) {
184 mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
Jeff Sharkeya4620792011-05-20 15:29:23 -0700185 }
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700186
Jeff Sharkeya4620792011-05-20 15:29:23 -0700187 public void systemReady() {
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700188 synchronized (mRulesLock) {
189 // read policy from disk
190 readPolicyLocked();
191 }
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700192
Jeff Sharkeya4620792011-05-20 15:29:23 -0700193 updateScreenOn();
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700194
Jeff Sharkeya4620792011-05-20 15:29:23 -0700195 try {
196 mActivityManager.registerProcessObserver(mProcessObserver);
197 } catch (RemoteException e) {
198 // ouch, no foregroundActivities updates means some processes may
199 // never get network access.
200 Slog.e(TAG, "unable to register IProcessObserver", e);
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700201 }
Jeff Sharkeya4620792011-05-20 15:29:23 -0700202
203 // TODO: traverse existing processes to know foreground state, or have
204 // activitymanager dispatch current state when new observer attached.
205
206 final IntentFilter screenFilter = new IntentFilter();
207 screenFilter.addAction(Intent.ACTION_SCREEN_ON);
208 screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
209 mContext.registerReceiver(mScreenReceiver, screenFilter);
210
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700211 // watch for network interfaces to be claimed
212 final IntentFilter ifaceFilter = new IntentFilter();
213 ifaceFilter.addAction(CONNECTIVITY_ACTION);
214 mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler);
215
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700216 }
217
Jeff Sharkeya4620792011-05-20 15:29:23 -0700218 private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
219 @Override
220 public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
221 // only someone like AMS should only be calling us
Jeff Sharkey75279902011-05-24 18:39:45 -0700222 mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG);
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700223
Jeff Sharkeya4620792011-05-20 15:29:23 -0700224 synchronized (mRulesLock) {
225 // because a uid can have multiple pids running inside, we need to
226 // remember all pid states and summarize foreground at uid level.
227
228 // record foreground for this specific pid
229 SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
230 if (pidForeground == null) {
231 pidForeground = new SparseBooleanArray(2);
232 mUidPidForeground.put(uid, pidForeground);
233 }
234 pidForeground.put(pid, foregroundActivities);
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700235 computeUidForegroundLocked(uid);
Jeff Sharkeya4620792011-05-20 15:29:23 -0700236 }
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700237 }
Jeff Sharkeya4620792011-05-20 15:29:23 -0700238
239 @Override
240 public void onProcessDied(int pid, int uid) {
241 // only someone like AMS should only be calling us
Jeff Sharkey75279902011-05-24 18:39:45 -0700242 mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG);
Jeff Sharkeya4620792011-05-20 15:29:23 -0700243
244 synchronized (mRulesLock) {
245 // clear records and recompute, when they exist
246 final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
247 if (pidForeground != null) {
248 pidForeground.delete(pid);
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700249 computeUidForegroundLocked(uid);
Jeff Sharkeya4620792011-05-20 15:29:23 -0700250 }
251 }
252 }
253 };
254
255 private BroadcastReceiver mScreenReceiver = new BroadcastReceiver() {
256 @Override
257 public void onReceive(Context context, Intent intent) {
258 synchronized (mRulesLock) {
259 // screen-related broadcasts are protected by system, no need
260 // for permissions check.
261 updateScreenOn();
262 }
263 }
264 };
265
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700266 /**
267 * Receiver that watches for {@link IConnectivityManager} to claim network
268 * interfaces. Used to apply {@link NetworkPolicy} when networks match
269 * {@link StrongTemplate}.
270 */
271 private BroadcastReceiver mIfaceReceiver = new BroadcastReceiver() {
272 @Override
273 public void onReceive(Context context, Intent intent) {
274 // on background handler thread, and verified CONNECTIVITY_INTERNAL
275 // permission above.
276 synchronized (mRulesLock) {
277 updateIfacesLocked();
278 }
279 }
280 };
281
282 /**
283 * Examine all connected {@link NetworkState}, looking for
284 * {@link NetworkPolicy} that need to be enforced. When matches found, set
285 * remaining quota based on usage cycle and historical stats.
286 */
287 private void updateIfacesLocked() {
288 if (LOGD) Slog.v(TAG, "updateIfacesLocked()");
289
290 final NetworkState[] states;
291 try {
292 states = mConnManager.getAllNetworkState();
293 } catch (RemoteException e) {
294 Slog.w(TAG, "problem reading network state");
295 return;
296 }
297
298 // first, derive identity for all connected networks, which can be used
299 // to match against templates.
300 final HashMap<NetworkIdentity, String> networks = Maps.newHashMap();
301 for (NetworkState state : states) {
302 // stash identity and iface away for later use
303 if (state.networkInfo.isConnected()) {
304 final String iface = state.linkProperties.getInterfaceName();
305 final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
306 networks.put(ident, iface);
307 }
308 }
309
310 // build list of rules and ifaces to enforce them against
311 final HashMap<StrongTemplate, String[]> rules = Maps.newHashMap();
312 final ArrayList<String> ifaceList = Lists.newArrayList();
313 for (StrongTemplate template : mTemplatePolicy.keySet()) {
314
315 // collect all active ifaces that match this template
316 ifaceList.clear();
317 for (NetworkIdentity ident : networks.keySet()) {
318 if (ident.matchesTemplate(template.networkTemplate, template.subscriberId)) {
319 final String iface = networks.get(ident);
320 ifaceList.add(iface);
321 }
322 }
323
324 if (ifaceList.size() > 0) {
325 final String[] ifaces = ifaceList.toArray(new String[ifaceList.size()]);
326 rules.put(template, ifaces);
327 }
328 }
329
330 // try refreshing time source when stale
331 if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
332 mTime.forceRefresh();
333 }
334
335 final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
336 : System.currentTimeMillis();
337
338 // apply each policy that we found ifaces for; compute remaining data
339 // based on current cycle and historical stats, and push to kernel.
340 for (StrongTemplate template : rules.keySet()) {
341 final NetworkPolicy policy = mTemplatePolicy.get(template);
342 final String[] ifaces = rules.get(policy);
343
344 final long start = computeLastCycleBoundary(currentTime, policy);
345 final long end = currentTime;
346
347 final NetworkStats stats;
348 try {
349 stats = mNetworkStats.getSummaryForNetwork(
350 start, end, template.networkTemplate, template.subscriberId);
351 } catch (RemoteException e) {
352 Slog.w(TAG, "problem reading summary for template " + template.networkTemplate);
353 continue;
354 }
355
356 // remaining "quota" is based on usage in current cycle
357 final long total = stats.rx[0] + stats.tx[0];
358 final long quota = Math.max(0, policy.limitBytes - total);
359
360 if (LOGD) {
361 Slog.d(TAG, "applying policy " + policy.toString() + " to ifaces "
362 + Arrays.toString(ifaces) + " with quota " + quota);
363 }
364
365 // TODO: push rule down through NetworkManagementService.setInterfaceQuota()
366
367 }
368 }
369
370 /**
371 * Compute the last cycle boundary for the given {@link NetworkPolicy}. For
372 * example, if cycle day is 20th, and today is June 15th, it will return May
373 * 20th. When cycle day doesn't exist in current month, it snaps to the 1st
374 * of following month.
375 */
376 // @VisibleForTesting
377 public static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) {
378 final Time now = new Time(Time.TIMEZONE_UTC);
379 now.set(currentTime);
380
381 // first, find cycle boundary for current month
382 final Time cycle = new Time(now);
383 cycle.hour = cycle.minute = cycle.second = 0;
384 snapToCycleDay(cycle, policy.cycleDay);
385
386 if (cycle.after(now)) {
387 // cycle boundary is beyond now, use last cycle boundary; start by
388 // pushing ourselves squarely into last month.
389 final Time lastMonth = new Time(now);
390 lastMonth.hour = lastMonth.minute = lastMonth.second = 0;
391 lastMonth.monthDay = 1;
392 lastMonth.month -= 1;
393 lastMonth.normalize(true);
394
395 cycle.set(lastMonth);
396 snapToCycleDay(cycle, policy.cycleDay);
397 }
398
399 return cycle.toMillis(true);
400 }
401
402 /**
403 * Snap to the cycle day for the current month given; when cycle day doesn't
404 * exist, it snaps to 1st of following month.
405 */
406 private static void snapToCycleDay(Time time, int cycleDay) {
407 if (cycleDay > time.getActualMaximum(MONTH_DAY)) {
408 // cycle day isn't valid this month; snap to 1st of next month
409 time.month += 1;
410 time.monthDay = 1;
411 } else {
412 time.monthDay = cycleDay;
413 }
414 time.normalize(true);
415 }
416
417 private void readPolicyLocked() {
418 if (LOGD) Slog.v(TAG, "readPolicyLocked()");
419
420 // clear any existing policy and read from disk
421 mTemplatePolicy.clear();
422 mUidPolicy.clear();
423
424 FileInputStream fis = null;
425 try {
426 fis = mPolicyFile.openRead();
427 final XmlPullParser in = Xml.newPullParser();
428 in.setInput(fis, null);
429
430 int type;
431 int version = VERSION_CURRENT;
432 while ((type = in.next()) != END_DOCUMENT) {
433 final String tag = in.getName();
434 if (type == START_TAG) {
435 if (TAG_POLICY_LIST.equals(tag)) {
436 version = readIntAttribute(in, ATTR_VERSION);
437
438 } else if (TAG_NETWORK_POLICY.equals(tag)) {
439 final int networkTemplate = readIntAttribute(in, ATTR_NETWORK_TEMPLATE);
440 final String subscriberId = in.getAttributeValue(null, ATTR_SUBSCRIBER_ID);
441
442 final int cycleDay = readIntAttribute(in, ATTR_CYCLE_DAY);
443 final long warningBytes = readLongAttribute(in, ATTR_WARNING_BYTES);
444 final long limitBytes = readLongAttribute(in, ATTR_LIMIT_BYTES);
445
446 mTemplatePolicy.put(new StrongTemplate(networkTemplate, subscriberId),
447 new NetworkPolicy(cycleDay, warningBytes, limitBytes));
448
449 } else if (TAG_UID_POLICY.equals(tag)) {
450 final int uid = readIntAttribute(in, ATTR_UID);
451 final int policy = readIntAttribute(in, ATTR_POLICY);
452
453 mUidPolicy.put(uid, policy);
454 }
455 }
456 }
457
458 } catch (FileNotFoundException e) {
459 // missing policy is okay, probably first boot
460 } catch (IOException e) {
461 Slog.e(TAG, "problem reading network stats", e);
462 } catch (XmlPullParserException e) {
463 Slog.e(TAG, "problem reading network stats", e);
464 } finally {
465 IoUtils.closeQuietly(fis);
466 }
467 }
468
469 private void writePolicyLocked() {
470 if (LOGD) Slog.v(TAG, "writePolicyLocked()");
471
472 FileOutputStream fos = null;
473 try {
474 fos = mPolicyFile.startWrite();
475
476 XmlSerializer out = new FastXmlSerializer();
477 out.setOutput(fos, "utf-8");
478 out.startDocument(null, true);
479
480 out.startTag(null, TAG_POLICY_LIST);
481 writeIntAttribute(out, ATTR_VERSION, VERSION_CURRENT);
482
483 // write all known network policies
484 for (StrongTemplate template : mTemplatePolicy.keySet()) {
485 final NetworkPolicy policy = mTemplatePolicy.get(template);
486
487 out.startTag(null, TAG_NETWORK_POLICY);
488 writeIntAttribute(out, ATTR_NETWORK_TEMPLATE, template.networkTemplate);
489 if (template.subscriberId != null) {
490 out.attribute(null, ATTR_SUBSCRIBER_ID, template.subscriberId);
491 }
492 writeIntAttribute(out, ATTR_CYCLE_DAY, policy.cycleDay);
493 writeLongAttribute(out, ATTR_WARNING_BYTES, policy.warningBytes);
494 writeLongAttribute(out, ATTR_LIMIT_BYTES, policy.limitBytes);
495 out.endTag(null, TAG_NETWORK_POLICY);
496 }
497
498 // write all known uid policies
499 for (int i = 0; i < mUidPolicy.size(); i++) {
500 final int uid = mUidPolicy.keyAt(i);
501 final int policy = mUidPolicy.valueAt(i);
502
503 out.startTag(null, TAG_UID_POLICY);
504 writeIntAttribute(out, ATTR_UID, uid);
505 writeIntAttribute(out, ATTR_POLICY, policy);
506 out.endTag(null, TAG_UID_POLICY);
507 }
508
509 out.endTag(null, TAG_POLICY_LIST);
510 out.endDocument();
511
512 mPolicyFile.finishWrite(fos);
513 } catch (IOException e) {
514 if (fos != null) {
515 mPolicyFile.failWrite(fos);
516 }
517 }
518 }
519
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700520 @Override
521 public void setUidPolicy(int uid, int policy) {
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700522 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
Jeff Sharkeya4620792011-05-20 15:29:23 -0700523
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -0700524 final int oldPolicy;
Jeff Sharkeya4620792011-05-20 15:29:23 -0700525 synchronized (mRulesLock) {
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -0700526 oldPolicy = getUidPolicy(uid);
Jeff Sharkeya4620792011-05-20 15:29:23 -0700527 mUidPolicy.put(uid, policy);
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -0700528
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700529 // uid policy changed, recompute rules and persist policy.
530 updateRulesForUidLocked(uid);
531 writePolicyLocked();
532 }
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700533 }
534
535 @Override
536 public int getUidPolicy(int uid) {
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700537 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
538
Jeff Sharkeya4620792011-05-20 15:29:23 -0700539 synchronized (mRulesLock) {
540 return mUidPolicy.get(uid, POLICY_NONE);
541 }
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700542 }
543
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -0700544 @Override
545 public void registerListener(INetworkPolicyListener listener) {
546 mListeners.register(listener);
547
548 synchronized (mRulesLock) {
549 // dispatch any existing rules to new listeners
550 final int size = mUidRules.size();
551 for (int i = 0; i < size; i++) {
552 final int uid = mUidRules.keyAt(i);
553 final int uidRules = mUidRules.valueAt(i);
554 if (uidRules != RULE_ALLOW_ALL) {
555 try {
556 listener.onRulesChanged(uid, uidRules);
557 } catch (RemoteException e) {
558 }
559 }
560 }
561 }
562 }
563
564 @Override
565 public void unregisterListener(INetworkPolicyListener listener) {
566 mListeners.unregister(listener);
567 }
568
Jeff Sharkey1b861272011-05-22 00:34:52 -0700569 @Override
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700570 public void setNetworkPolicy(int networkType, String subscriberId, NetworkPolicy policy) {
571 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
572
573 synchronized (mRulesLock) {
574 mTemplatePolicy.put(new StrongTemplate(networkType, subscriberId), policy);
575
576 // network policy changed, recompute template rules based on active
577 // interfaces and persist policy.
578 updateIfacesLocked();
579 writePolicyLocked();
580 }
581 }
582
583 @Override
584 public NetworkPolicy getNetworkPolicy(int networkType, String subscriberId) {
585 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
586
587 synchronized (mRulesLock) {
588 return mTemplatePolicy.get(new StrongTemplate(networkType, subscriberId));
589 }
590 }
591
592 @Override
Jeff Sharkey1b861272011-05-22 00:34:52 -0700593 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Jeff Sharkey75279902011-05-24 18:39:45 -0700594 mContext.enforceCallingOrSelfPermission(DUMP, TAG);
Jeff Sharkey1b861272011-05-22 00:34:52 -0700595
596 synchronized (mRulesLock) {
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700597 fout.println("Network policies:");
598 for (StrongTemplate template : mTemplatePolicy.keySet()) {
599 final NetworkPolicy policy = mTemplatePolicy.get(template);
600 fout.print(" "); fout.println(template.toString());
601 fout.print(" "); fout.println(policy.toString());
602 }
603
Jeff Sharkey1b861272011-05-22 00:34:52 -0700604 fout.println("Policy status for known UIDs:");
605
606 final SparseBooleanArray knownUids = new SparseBooleanArray();
607 collectKeys(mUidPolicy, knownUids);
608 collectKeys(mUidForeground, knownUids);
609 collectKeys(mUidRules, knownUids);
610
611 final int size = knownUids.size();
612 for (int i = 0; i < size; i++) {
613 final int uid = knownUids.keyAt(i);
614 fout.print(" UID=");
615 fout.print(uid);
616
617 fout.print(" policy=");
618 final int policyIndex = mUidPolicy.indexOfKey(uid);
619 if (policyIndex < 0) {
620 fout.print("UNKNOWN");
621 } else {
622 dumpPolicy(fout, mUidPolicy.valueAt(policyIndex));
623 }
624
625 fout.print(" foreground=");
626 final int foregroundIndex = mUidPidForeground.indexOfKey(uid);
627 if (foregroundIndex < 0) {
628 fout.print("UNKNOWN");
629 } else {
630 dumpSparseBooleanArray(fout, mUidPidForeground.valueAt(foregroundIndex));
631 }
632
633 fout.print(" rules=");
634 final int rulesIndex = mUidRules.indexOfKey(uid);
635 if (rulesIndex < 0) {
636 fout.print("UNKNOWN");
637 } else {
638 dumpRules(fout, mUidRules.valueAt(rulesIndex));
639 }
640
641 fout.println();
642 }
643 }
644 }
Jeff Sharkey9599cc52011-05-22 14:59:31 -0700645
646 @Override
647 public boolean isUidForeground(int uid) {
648 synchronized (mRulesLock) {
649 // only really in foreground when screen is also on
650 return mUidForeground.get(uid, false) && mScreenOn;
651 }
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -0700652 }
653
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700654 /**
655 * Foreground for PID changed; recompute foreground at UID level. If
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700656 * changed, will trigger {@link #updateRulesForUidLocked(int)}.
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700657 */
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700658 private void computeUidForegroundLocked(int uid) {
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700659 final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
660
661 // current pid is dropping foreground; examine other pids
662 boolean uidForeground = false;
663 final int size = pidForeground.size();
664 for (int i = 0; i < size; i++) {
665 if (pidForeground.valueAt(i)) {
666 uidForeground = true;
667 break;
668 }
669 }
670
671 final boolean oldUidForeground = mUidForeground.get(uid, false);
672 if (oldUidForeground != uidForeground) {
673 // foreground changed, push updated rules
674 mUidForeground.put(uid, uidForeground);
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700675 updateRulesForUidLocked(uid);
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700676 }
677 }
678
Jeff Sharkeya4620792011-05-20 15:29:23 -0700679 private void updateScreenOn() {
680 synchronized (mRulesLock) {
681 try {
682 mScreenOn = mPowerManager.isScreenOn();
683 } catch (RemoteException e) {
684 }
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700685 updateRulesForScreenLocked();
Jeff Sharkeya4620792011-05-20 15:29:23 -0700686 }
687 }
688
689 /**
690 * Update rules that might be changed by {@link #mScreenOn} value.
691 */
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700692 private void updateRulesForScreenLocked() {
Jeff Sharkeya4620792011-05-20 15:29:23 -0700693 // only update rules for anyone with foreground activities
694 final int size = mUidForeground.size();
695 for (int i = 0; i < size; i++) {
696 if (mUidForeground.valueAt(i)) {
697 final int uid = mUidForeground.keyAt(i);
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700698 updateRulesForUidLocked(uid);
Jeff Sharkeya4620792011-05-20 15:29:23 -0700699 }
700 }
701 }
702
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700703 private void updateRulesForUidLocked(int uid) {
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700704 final int uidPolicy = getUidPolicy(uid);
Jeff Sharkey9599cc52011-05-22 14:59:31 -0700705 final boolean uidForeground = isUidForeground(uid);
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700706
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -0700707 // derive active rules based on policy and active state
708 int uidRules = RULE_ALLOW_ALL;
709 if (!uidForeground && (uidPolicy & POLICY_REJECT_PAID_BACKGROUND) != 0) {
710 // uid in background, and policy says to block paid data
711 uidRules = RULE_REJECT_PAID;
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700712 }
713
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -0700714 // TODO: only dispatch when rules actually change
715
716 // record rule locally to dispatch to new listeners
717 mUidRules.put(uid, uidRules);
718
719 // dispatch changed rule to existing listeners
720 final int length = mListeners.beginBroadcast();
721 for (int i = 0; i < length; i++) {
722 final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
723 if (listener != null) {
724 try {
725 listener.onRulesChanged(uid, uidRules);
726 } catch (RemoteException e) {
727 }
728 }
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700729 }
Jeff Sharkeyc006f1a2011-05-19 17:12:49 -0700730 mListeners.finishBroadcast();
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700731 }
732
Jeff Sharkey1b861272011-05-22 00:34:52 -0700733 private static void collectKeys(SparseIntArray source, SparseBooleanArray target) {
734 final int size = source.size();
735 for (int i = 0; i < size; i++) {
736 target.put(source.keyAt(i), true);
737 }
738 }
739
740 private static void collectKeys(SparseBooleanArray source, SparseBooleanArray target) {
741 final int size = source.size();
742 for (int i = 0; i < size; i++) {
743 target.put(source.keyAt(i), true);
744 }
745 }
746
747 private static void dumpSparseBooleanArray(PrintWriter fout, SparseBooleanArray value) {
748 fout.print("[");
749 final int size = value.size();
750 for (int i = 0; i < size; i++) {
751 fout.print(value.keyAt(i) + "=" + value.valueAt(i));
752 if (i < size - 1) fout.print(",");
753 }
754 fout.print("]");
755 }
Jeff Sharkey21c9c452011-06-07 12:26:43 -0700756
757 private static int readIntAttribute(XmlPullParser in, String name) throws IOException {
758 final String value = in.getAttributeValue(null, name);
759 try {
760 return Integer.parseInt(value);
761 } catch (NumberFormatException e) {
762 throw new ProtocolException("problem parsing " + name + "=" + value + " as int");
763 }
764 }
765
766 private static long readLongAttribute(XmlPullParser in, String name) throws IOException {
767 final String value = in.getAttributeValue(null, name);
768 try {
769 return Long.parseLong(value);
770 } catch (NumberFormatException e) {
771 throw new ProtocolException("problem parsing " + name + "=" + value + " as int");
772 }
773 }
774
775 private static void writeIntAttribute(XmlSerializer out, String name, int value)
776 throws IOException {
777 out.attribute(null, name, Integer.toString(value));
778 }
779
780 private static void writeLongAttribute(XmlSerializer out, String name, long value)
781 throws IOException {
782 out.attribute(null, name, Long.toString(value));
783 }
784
785 /**
786 * Network template with strong subscriber ID, used as key when defining
787 * {@link NetworkPolicy}.
788 */
789 private static class StrongTemplate {
790 public final int networkTemplate;
791 public final String subscriberId;
792
793 public StrongTemplate(int networkTemplate, String subscriberId) {
794 this.networkTemplate = networkTemplate;
795 this.subscriberId = subscriberId;
796 }
797
798 @Override
799 public int hashCode() {
800 return Objects.hashCode(networkTemplate, subscriberId);
801 }
802
803 @Override
804 public boolean equals(Object obj) {
805 if (obj instanceof StrongTemplate) {
806 final StrongTemplate template = (StrongTemplate) obj;
807 return template.networkTemplate == networkTemplate
808 && Objects.equal(template.subscriberId, subscriberId);
809 }
810 return false;
811 }
812
813 @Override
814 public String toString() {
815 return "TemplateIdentity: networkTemplate=" + networkTemplate + ", subscriberId="
816 + subscriberId;
817 }
818
819 }
820
Jeff Sharkeyd5cdd592011-05-03 20:27:17 -0700821}