/** Control UID policies. */
void setUidPolicy(int uid, int policy);
+ void addUidPolicy(int uid, int policy);
+ void removeUidPolicy(int uid, int policy);
int getUidPolicy(int uid);
int[] getUidsWithPolicy(int policy);
boolean isUidForeground(int uid);
+ int[] getPowerSaveAppIdWhitelist();
+
void registerListener(INetworkPolicyListener listener);
void unregisterListener(INetworkPolicyListener listener);
public static final int POLICY_NONE = 0x0;
/** Reject network usage on metered networks when application in background. */
public static final int POLICY_REJECT_METERED_BACKGROUND = 0x1;
+ /** Allow network use (metered or not) in the background in battery save mode. */
+ public static final int POLICY_ALLOW_BACKGROUND_BATTERY_SAVE = 0x2;
/** All network traffic should be allowed. */
public static final int RULE_ALLOW_ALL = 0x0;
* Set policy flags for specific UID.
*
* @param policy {@link #POLICY_NONE} or combination of flags like
- * {@link #POLICY_REJECT_METERED_BACKGROUND}.
+ * {@link #POLICY_REJECT_METERED_BACKGROUND}, {@link #POLICY_ALLOW_BACKGROUND_BATTERY_SAVE}.
*/
public void setUidPolicy(int uid, int policy) {
try {
}
}
+ /**
+ * Add policy flags for specific UID. The given policy bits will be set for
+ * the uid. Policy flags may be either
+ * {@link #POLICY_REJECT_METERED_BACKGROUND} or {@link #POLICY_ALLOW_BACKGROUND_BATTERY_SAVE}.
+ */
+ public void addUidPolicy(int uid, int policy) {
+ try {
+ mService.addUidPolicy(uid, policy);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Clear/remove policy flags for specific UID. The given policy bits will be set for
+ * the uid. Policy flags may be either
+ * {@link #POLICY_REJECT_METERED_BACKGROUND} or {@link #POLICY_ALLOW_BACKGROUND_BATTERY_SAVE}.
+ */
+ public void removeUidPolicy(int uid, int policy) {
+ try {
+ mService.removeUidPolicy(uid, policy);
+ } catch (RemoteException e) {
+ }
+ }
+
public int getUidPolicy(int uid) {
try {
return mService.getUidPolicy(uid);
}
}
+ public int[] getPowerSaveAppIdWhitelist() {
+ try {
+ return mService.getPowerSaveAppIdWhitelist();
+ } catch (RemoteException e) {
+ return new int[0];
+ }
+ }
+
public void registerListener(INetworkPolicyListener listener) {
try {
mService.registerListener(listener);
<library name="javax.obex"
file="/system/framework/javax.obex.jar"/>
+ <!-- These are the standard packages that are white-listed to always have internet
+ access while in power save mode, even if they aren't in the foreground. -->
+ <allow-in-power-save package="com.android.providers.downloads" />
+
</permissions>
--- /dev/null
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.pm.FeatureInfo;
+import android.os.*;
+import android.os.Process;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import static com.android.internal.util.ArrayUtils.appendInt;
+
+/**
+ * Loads global system configuration info.
+ */
+public class SystemConfig {
+ static final String TAG = "SystemConfig";
+
+ static SystemConfig sInstance;
+
+ // Group-ids that are given to all packages as read from etc/permissions/*.xml.
+ int[] mGlobalGids;
+
+ // These are the built-in uid -> permission mappings that were read from the
+ // system configuration files.
+ final SparseArray<HashSet<String>> mSystemPermissions =
+ new SparseArray<HashSet<String>>();
+
+ // These are the built-in shared libraries that were read from the
+ // system configuration files. Keys are the library names; strings are the
+ // paths to the libraries.
+ final ArrayMap<String, String> mSharedLibraries
+ = new ArrayMap<String, String>();
+
+ // These are the features this devices supports that were read from the
+ // system configuration files.
+ final HashMap<String, FeatureInfo> mAvailableFeatures =
+ new HashMap<String, FeatureInfo>();
+
+ public static final class PermissionEntry {
+ public final String name;
+ public int[] gids;
+
+ PermissionEntry(String _name) {
+ name = _name;
+ }
+ }
+
+ // These are the permission -> gid mappings that were read from the
+ // system configuration files.
+ final ArrayMap<String, PermissionEntry> mPermissions =
+ new ArrayMap<String, PermissionEntry>();
+
+ // These are the packages that are white-listed to be able to run in the
+ // background while in power save mode, as read from the configuration files.
+ final ArraySet<String> mAllowInPowerSave = new ArraySet<String>();
+
+ public static SystemConfig getInstance() {
+ synchronized (SystemConfig.class) {
+ if (sInstance == null) {
+ sInstance = new SystemConfig();
+ }
+ return sInstance;
+ }
+ }
+
+ public int[] getGlobalGids() {
+ return mGlobalGids;
+ }
+
+ public SparseArray<HashSet<String>> getSystemPermissions() {
+ return mSystemPermissions;
+ }
+
+ public ArrayMap<String, String> getSharedLibraries() {
+ return mSharedLibraries;
+ }
+
+ public HashMap<String, FeatureInfo> getAvailableFeatures() {
+ return mAvailableFeatures;
+ }
+
+ public ArrayMap<String, PermissionEntry> getPermissions() {
+ return mPermissions;
+ }
+
+ public ArraySet<String> getAllowInPowerSave() {
+ return mAllowInPowerSave;
+ }
+
+ SystemConfig() {
+ // Read configuration from system
+ readPermissions(Environment.buildPath(
+ Environment.getRootDirectory(), "etc", "sysconfig"), false);
+ // Read configuration from the old permissions dir
+ readPermissions(Environment.buildPath(
+ Environment.getRootDirectory(), "etc", "permissions"), false);
+ // Only read features from OEM config
+ readPermissions(Environment.buildPath(
+ Environment.getOemDirectory(), "etc", "sysconfig"), true);
+ readPermissions(Environment.buildPath(
+ Environment.getOemDirectory(), "etc", "permissions"), true);
+ }
+
+ void readPermissions(File libraryDir, boolean onlyFeatures) {
+ // Read permissions from given directory.
+ if (!libraryDir.exists() || !libraryDir.isDirectory()) {
+ if (!onlyFeatures) {
+ Slog.w(TAG, "No directory " + libraryDir + ", skipping");
+ }
+ return;
+ }
+ if (!libraryDir.canRead()) {
+ Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
+ return;
+ }
+
+ // Iterate over the files in the directory and scan .xml files
+ for (File f : libraryDir.listFiles()) {
+ // We'll read platform.xml last
+ if (f.getPath().endsWith("etc/permissions/platform.xml")) {
+ continue;
+ }
+
+ if (!f.getPath().endsWith(".xml")) {
+ Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
+ continue;
+ }
+ if (!f.canRead()) {
+ Slog.w(TAG, "Permissions library file " + f + " cannot be read");
+ continue;
+ }
+
+ readPermissionsFromXml(f, onlyFeatures);
+ }
+
+ // Read permissions from .../etc/permissions/platform.xml last so it will take precedence
+ final File permFile = new File(Environment.getRootDirectory(),
+ "etc/permissions/platform.xml");
+ readPermissionsFromXml(permFile, onlyFeatures);
+ }
+
+ private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
+ FileReader permReader = null;
+ try {
+ permReader = new FileReader(permFile);
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
+ return;
+ }
+
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(permReader);
+
+ int type;
+ while ((type=parser.next()) != parser.START_TAG
+ && type != parser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != parser.START_TAG) {
+ throw new XmlPullParserException("No start tag found");
+ }
+
+ if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
+ throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
+ ", expected 'permissions' or 'config'");
+ }
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+ if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+ break;
+ }
+
+ String name = parser.getName();
+ if ("group".equals(name) && !onlyFeatures) {
+ String gidStr = parser.getAttributeValue(null, "gid");
+ if (gidStr != null) {
+ int gid = android.os.Process.getGidForName(gidStr);
+ mGlobalGids = appendInt(mGlobalGids, gid);
+ } else {
+ Slog.w(TAG, "<group> without gid at "
+ + parser.getPositionDescription());
+ }
+
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ } else if ("permission".equals(name) && !onlyFeatures) {
+ String perm = parser.getAttributeValue(null, "name");
+ if (perm == null) {
+ Slog.w(TAG, "<permission> without name at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ perm = perm.intern();
+ readPermission(parser, perm);
+
+ } else if ("assign-permission".equals(name) && !onlyFeatures) {
+ String perm = parser.getAttributeValue(null, "name");
+ if (perm == null) {
+ Slog.w(TAG, "<assign-permission> without name at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ String uidStr = parser.getAttributeValue(null, "uid");
+ if (uidStr == null) {
+ Slog.w(TAG, "<assign-permission> without uid at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ int uid = Process.getUidForName(uidStr);
+ if (uid < 0) {
+ Slog.w(TAG, "<assign-permission> with unknown uid \""
+ + uidStr + "\" at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ perm = perm.intern();
+ HashSet<String> perms = mSystemPermissions.get(uid);
+ if (perms == null) {
+ perms = new HashSet<String>();
+ mSystemPermissions.put(uid, perms);
+ }
+ perms.add(perm);
+ XmlUtils.skipCurrentTag(parser);
+
+ } else if ("library".equals(name) && !onlyFeatures) {
+ String lname = parser.getAttributeValue(null, "name");
+ String lfile = parser.getAttributeValue(null, "file");
+ if (lname == null) {
+ Slog.w(TAG, "<library> without name at "
+ + parser.getPositionDescription());
+ } else if (lfile == null) {
+ Slog.w(TAG, "<library> without file at "
+ + parser.getPositionDescription());
+ } else {
+ //Log.i(TAG, "Got library " + lname + " in " + lfile);
+ mSharedLibraries.put(lname, lfile);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+
+ } else if ("feature".equals(name)) {
+ String fname = parser.getAttributeValue(null, "name");
+ if (fname == null) {
+ Slog.w(TAG, "<feature> without name at "
+ + parser.getPositionDescription());
+ } else {
+ //Log.i(TAG, "Got feature " + fname);
+ FeatureInfo fi = new FeatureInfo();
+ fi.name = fname;
+ mAvailableFeatures.put(fname, fi);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+
+ } else if ("allow-in-power-save".equals(name)) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<allow-in-power-save> without package at "
+ + parser.getPositionDescription());
+ } else {
+ mAllowInPowerSave.add(pkgname);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+
+ }
+ permReader.close();
+ } catch (XmlPullParserException e) {
+ Slog.w(TAG, "Got execption parsing permissions.", e);
+ } catch (IOException e) {
+ Slog.w(TAG, "Got execption parsing permissions.", e);
+ }
+ }
+
+ void readPermission(XmlPullParser parser, String name)
+ throws IOException, XmlPullParserException {
+
+ name = name.intern();
+
+ PermissionEntry perm = mPermissions.get(name);
+ if (perm == null) {
+ perm = new PermissionEntry(name);
+ mPermissions.put(name, perm);
+ }
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG
+ || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if ("group".equals(tagName)) {
+ String gidStr = parser.getAttributeValue(null, "gid");
+ if (gidStr != null) {
+ int gid = Process.getGidForName(gidStr);
+ perm.gids = appendInt(perm.gids, gid);
+ } else {
+ Slog.w(TAG, "<group> without gid at "
+ + parser.getPositionDescription());
+ }
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+}
import android.app.IAppTask;
import android.app.admin.DevicePolicyManager;
import android.appwidget.AppWidgetManager;
-import android.content.DialogInterface.OnClickListener;
-import android.content.res.Resources;
-import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.os.BatteryStats;
import android.os.PersistableBundle;
import android.os.UpdateLock;
import android.os.UserHandle;
import android.provider.Settings;
-import android.text.Spannable;
-import android.text.SpannableString;
import android.text.format.DateUtils;
import android.text.format.Time;
-import android.text.style.DynamicDrawableSpan;
-import android.text.style.ImageSpan;
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
-import android.widget.TextView;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_ALLOW_BACKGROUND_BATTERY_SAVE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import android.os.IPowerManager;
import android.os.Message;
import android.os.MessageQueue.IdleHandler;
+import android.os.PowerManagerInternal;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
import android.text.format.Formatter;
import android.text.format.Time;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.NtpTrustedTime;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import libcore.io.IoUtils;
private IConnectivityManager mConnManager;
private INotificationManager mNotifManager;
+ private PowerManagerInternal mPowerManagerInternal;
private final Object mRulesLock = new Object();
private volatile boolean mScreenOn;
private volatile boolean mRestrictBackground;
+ private volatile boolean mRestrictPower;
private final boolean mSuppressDefaultPolicy;
/** Defined network policies. */
- private HashMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = Maps.newHashMap();
+ private final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<
+ NetworkTemplate, NetworkPolicy>();
/** Currently active network rules for ifaces. */
- private HashMap<NetworkPolicy, String[]> mNetworkRules = Maps.newHashMap();
+ private final ArrayMap<NetworkPolicy, String[]> mNetworkRules = new ArrayMap<
+ NetworkPolicy, String[]>();
/** Defined UID policies. */
- private SparseIntArray mUidPolicy = new SparseIntArray();
+ private final SparseIntArray mUidPolicy = new SparseIntArray();
/** Currently derived rules for each UID. */
- private SparseIntArray mUidRules = new SparseIntArray();
+ private final SparseIntArray mUidRules = new SparseIntArray();
+
+ /** UIDs that have been white-listed to always be able to have network access in
+ * power save mode. */
+ private final SparseBooleanArray mPowerSaveWhitelistAppIds = new SparseBooleanArray();
/** Set of ifaces that are metered. */
private HashSet<String> mMeteredIfaces = Sets.newHashSet();
/** Set of over-limit templates that have been notified. */
- private HashSet<NetworkTemplate> mOverLimitNotified = Sets.newHashSet();
+ private final HashSet<NetworkTemplate> mOverLimitNotified = Sets.newHashSet();
/** Set of currently active {@link Notification} tags. */
- private HashSet<String> mActiveNotifs = Sets.newHashSet();
+ private final HashSet<String> mActiveNotifs = Sets.newHashSet();
/** Foreground at both UID and PID granularity. */
- private SparseBooleanArray mUidForeground = new SparseBooleanArray();
- private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
+ private final SparseBooleanArray mUidForeground = new SparseBooleanArray();
+ private final SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
SparseBooleanArray>();
private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<
return;
}
+ final PackageManager pm = mContext.getPackageManager();
+
synchronized (mRulesLock) {
+ SystemConfig sysConfig = SystemConfig.getInstance();
+ ArraySet<String> allowPower = sysConfig.getAllowInPowerSave();
+ for (int i=0; i<allowPower.size(); i++) {
+ String pkg = allowPower.valueAt(i);
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
+ if ((ai.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+ mPowerSaveWhitelistAppIds.put(UserHandle.getAppId(ai.uid), true);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+
+ mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ mPowerManagerInternal.registerLowPowerModeObserver(
+ new PowerManagerInternal.LowPowerModeListener() {
+ @Override
+ public void onLowPowerModeChanged(boolean enabled) {
+ synchronized (mRulesLock) {
+ if (mRestrictPower != enabled) {
+ mRestrictPower = enabled;
+ updateRulesForGlobalChangeLocked(true);
+ }
+ }
+ }
+ });
+ mRestrictPower = mPowerManagerInternal.getLowPowerModeEnabled();
+
// read policy from disk
readPolicyLocked();
- if (mRestrictBackground) {
- updateRulesForRestrictBackgroundLocked();
+ if (mRestrictBackground || mRestrictPower) {
+ updateRulesForGlobalChangeLocked(true);
updateNotificationsLocked();
}
}
// Update global restrict for new user
synchronized (mRulesLock) {
- updateRulesForRestrictBackgroundLocked();
+ updateRulesForGlobalChangeLocked(true);
}
}
};
// examine stats for each active policy
final long currentTime = currentTimeMillis();
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
+ for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+ final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
// ignore policies that aren't relevant to user
if (!isTemplateRelevant(policy.template)) continue;
if (!policy.hasCycle()) continue;
// completely, which is currently rare case.
final long currentTime = currentTimeMillis();
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
+ for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+ final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
// shortcut when policy has no limit
if (policy.limitBytes == LIMIT_DISABLED || !policy.hasCycle()) {
setNetworkTemplateEnabled(policy.template, true);
return;
}
+ // If we are in restrict power mode, we want to treat all interfaces
+ // as metered, to restrict access to the network by uid. However, we
+ // will not have a bandwidth limit. Also only do this if restrict
+ // background data use is *not* enabled, since that takes precendence
+ // use over those networks can have a cost associated with it).
+ final boolean powerSave = mRestrictPower && !mRestrictBackground;
+
// first, derive identity for all connected networks, which can be used
// to match against templates.
- final HashMap<NetworkIdentity, String> networks = Maps.newHashMap();
+ final ArrayMap<NetworkIdentity, String> networks = new ArrayMap();
+ final ArraySet<String> connIfaces = new ArraySet<String>();
for (NetworkState state : states) {
// stash identity and iface away for later use
if (state.networkInfo.isConnected()) {
final String iface = state.linkProperties.getInterfaceName();
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
networks.put(ident, iface);
+ if (powerSave) {
+ connIfaces.add(iface);
+ }
}
}
// build list of rules and ifaces to enforce them against
mNetworkRules.clear();
final ArrayList<String> ifaceList = Lists.newArrayList();
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
+ for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+ final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
// collect all active ifaces that match this template
ifaceList.clear();
- for (Map.Entry<NetworkIdentity, String> entry : networks.entrySet()) {
- final NetworkIdentity ident = entry.getKey();
+ for (int j = networks.size()-1; j >= 0; j--) {
+ final NetworkIdentity ident = networks.keyAt(j);
if (policy.template.matches(ident)) {
- final String iface = entry.getValue();
+ final String iface = networks.valueAt(j);
ifaceList.add(iface);
}
}
// apply each policy that we found ifaces for; compute remaining data
// based on current cycle and historical stats, and push to kernel.
final long currentTime = currentTimeMillis();
- for (NetworkPolicy policy : mNetworkRules.keySet()) {
- final String[] ifaces = mNetworkRules.get(policy);
+ for (int i = mNetworkRules.size()-1; i >= 0; i--) {
+ final NetworkPolicy policy = mNetworkRules.keyAt(i);
+ final String[] ifaces = mNetworkRules.valueAt(i);
final long start;
final long totalBytes;
removeInterfaceQuota(iface);
setInterfaceQuota(iface, quotaBytes);
newMeteredIfaces.add(iface);
+ if (powerSave) {
+ connIfaces.remove(iface);
+ }
}
}
}
}
+ for (int i = connIfaces.size()-1; i >= 0; i--) {
+ String iface = connIfaces.valueAt(i);
+ removeInterfaceQuota(iface);
+ setInterfaceQuota(iface, Long.MAX_VALUE);
+ newMeteredIfaces.add(iface);
+ }
+
mHandler.obtainMessage(MSG_ADVISE_PERSIST_THRESHOLD, lowestRule).sendToTarget();
// remove quota on any trailing interfaces
// examine to see if any policy is defined for active mobile
boolean mobileDefined = false;
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
- if (policy.template.matches(probeIdent)) {
+ for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+ if (mNetworkPolicy.valueAt(i).template.matches(probeIdent)) {
mobileDefined = true;
+ break;
}
}
final int policy = readIntAttribute(in, ATTR_POLICY);
if (UserHandle.isApp(uid)) {
- setUidPolicyUnchecked(uid, policy, false);
+ setUidPolicyUncheckedLocked(uid, policy, false);
} else {
Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
}
// TODO: set for other users during upgrade
final int uid = UserHandle.getUid(UserHandle.USER_OWNER, appId);
if (UserHandle.isApp(uid)) {
- setUidPolicyUnchecked(uid, policy, false);
+ setUidPolicyUncheckedLocked(uid, policy, false);
} else {
Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
}
writeBooleanAttribute(out, ATTR_RESTRICT_BACKGROUND, mRestrictBackground);
// write all known network policies
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
+ for (int i = 0; i < mNetworkPolicy.size(); i++) {
+ final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
final NetworkTemplate template = policy.template;
out.startTag(null, TAG_NETWORK_POLICY);
throw new IllegalArgumentException("cannot apply policy to UID " + uid);
}
- setUidPolicyUnchecked(uid, policy, true);
+ synchronized (mRulesLock) {
+ final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+ if (oldPolicy != policy) {
+ setUidPolicyUncheckedLocked(uid, policy, true);
+ }
+ }
}
- private void setUidPolicyUnchecked(int uid, int policy, boolean persist) {
- final int oldPolicy;
+ @Override
+ public void addUidPolicy(int uid, int policy) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ if (!UserHandle.isApp(uid)) {
+ throw new IllegalArgumentException("cannot apply policy to UID " + uid);
+ }
+
synchronized (mRulesLock) {
- oldPolicy = getUidPolicy(uid);
- mUidPolicy.put(uid, policy);
+ final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+ policy |= oldPolicy;
+ if (oldPolicy != policy) {
+ setUidPolicyUncheckedLocked(uid, policy, true);
+ }
+ }
+ }
- // uid policy changed, recompute rules and persist policy.
- updateRulesForUidLocked(uid);
- if (persist) {
- writePolicyLocked();
+ @Override
+ public void removeUidPolicy(int uid, int policy) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ if (!UserHandle.isApp(uid)) {
+ throw new IllegalArgumentException("cannot apply policy to UID " + uid);
+ }
+
+ synchronized (mRulesLock) {
+ final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+ policy = oldPolicy & ~policy;
+ if (oldPolicy != policy) {
+ setUidPolicyUncheckedLocked(uid, policy, true);
}
}
}
+ private void setUidPolicyUncheckedLocked(int uid, int policy, boolean persist) {
+ mUidPolicy.put(uid, policy);
+
+ // uid policy changed, recompute rules and persist policy.
+ updateRulesForUidLocked(uid);
+ if (persist) {
+ writePolicyLocked();
+ }
+ }
+
@Override
public int getUidPolicy(int uid) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
return uids;
}
+ @Override
+ public int[] getPowerSaveAppIdWhitelist() {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ synchronized (mRulesLock) {
+ int size = mPowerSaveWhitelistAppIds.size();
+ int[] appids = new int[size];
+ for (int i = 0; i < size; i++) {
+ appids[i] = mPowerSaveWhitelistAppIds.keyAt(i);
+ }
+ return appids;
+ }
+ }
+
/**
* Remove any policies associated with given {@link UserHandle}, persisting
* if any changes are made.
maybeRefreshTrustedTime();
synchronized (mRulesLock) {
mRestrictBackground = restrictBackground;
- updateRulesForRestrictBackgroundLocked();
+ updateRulesForGlobalChangeLocked(false);
updateNotificationsLocked();
writePolicyLocked();
}
}
private NetworkPolicy findPolicyForNetworkLocked(NetworkIdentity ident) {
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
+ for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+ NetworkPolicy policy = mNetworkPolicy.valueAt(i);
if (policy.template.matches(ident)) {
return policy;
}
synchronized (mRulesLock) {
if (argSet.contains("--unsnooze")) {
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
- policy.clearSnooze();
+ for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+ mNetworkPolicy.valueAt(i).clearSnooze();
}
updateNetworkEnabledLocked();
}
fout.print("Restrict background: "); fout.println(mRestrictBackground);
+ fout.print("Restrict power: "); fout.println(mRestrictPower);
fout.println("Network policies:");
fout.increaseIndent();
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
- fout.println(policy.toString());
+ for (int i = 0; i < mNetworkPolicy.size(); i++) {
+ fout.println(mNetworkPolicy.valueAt(i).toString());
}
fout.decreaseIndent();
}
fout.decreaseIndent();
+ size = mPowerSaveWhitelistAppIds.size();
+ if (size > 0) {
+ fout.println("Power save whitelist app ids:");
+ fout.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ fout.print("UID=");
+ fout.print(mPowerSaveWhitelistAppIds.keyAt(i));
+ fout.print(": ");
+ fout.print(mPowerSaveWhitelistAppIds.valueAt(i));
+ fout.println();
+ }
+ fout.decreaseIndent();
+ }
+
final SparseBooleanArray knownUids = new SparseBooleanArray();
collectKeys(mUidForeground, knownUids);
collectKeys(mUidRules, knownUids);
}
/**
- * Update rules that might be changed by {@link #mRestrictBackground} value.
+ * Update rules that might be changed by {@link #mRestrictBackground}
+ * or {@link #mRestrictPower} value.
*/
- private void updateRulesForRestrictBackgroundLocked() {
+ private void updateRulesForGlobalChangeLocked(boolean restrictedNetworksChanged) {
final PackageManager pm = mContext.getPackageManager();
final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
// limit data usage for some internal system services
updateRulesForUidLocked(android.os.Process.MEDIA_UID);
updateRulesForUidLocked(android.os.Process.DRM_UID);
+
+ // If the set of restricted networks may have changed, re-evaluate those.
+ if (restrictedNetworksChanged) {
+ updateNetworkRulesLocked();
+ }
}
private static boolean isUidValidForRules(int uid) {
if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
// uid in background, and policy says to block metered data
uidRules = RULE_REJECT_METERED;
- }
- if (!uidForeground && mRestrictBackground) {
- // uid in background, and global background disabled
- uidRules = RULE_REJECT_METERED;
+ } else if (mRestrictBackground) {
+ if (!uidForeground) {
+ // uid in background, and global background disabled
+ uidRules = RULE_REJECT_METERED;
+ }
+ } else if (mRestrictPower) {
+ final boolean whitelisted = mPowerSaveWhitelistAppIds.get(UserHandle.getAppId(uid));
+ if (!whitelisted && !uidForeground
+ && (uidPolicy & POLICY_ALLOW_BACKGROUND_BATTERY_SAVE) == 0) {
+ // uid is in background, restrict power use mode is on (so we want to
+ // restrict all background network access), and this uid is not on the
+ // white list of those allowed background access.
+ uidRules = RULE_REJECT_METERED;
+ }
}
// TODO: only dispatch when rules actually change
import static com.android.internal.util.ArrayUtils.appendInt;
import static com.android.internal.util.ArrayUtils.removeInt;
+import android.util.ArrayMap;
import com.android.internal.R;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
import com.android.server.IntentResolver;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
+import com.android.server.SystemConfig;
import com.android.server.Watchdog;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.storage.DeviceStorageMonitorInternal;
final Settings mSettings;
boolean mRestoredSettings;
- // Group-ids that are given to all packages as read from etc/permissions/*.xml.
- int[] mGlobalGids;
+ // System configuration read by SystemConfig.
+ final int[] mGlobalGids;
+ final SparseArray<HashSet<String>> mSystemPermissions;
+ final HashMap<String, FeatureInfo> mAvailableFeatures;
- // These are the built-in uid -> permission mappings that were read from the
- // etc/permissions.xml file.
- final SparseArray<HashSet<String>> mSystemPermissions =
- new SparseArray<HashSet<String>>();
+ // If mac_permissions.xml was found for seinfo labeling.
+ boolean mFoundPolicyFile;
+
+ // If a recursive restorecon of /data/data/<pkg> is needed.
+ private boolean mShouldRestoreconData = SELinuxMMAC.shouldRestorecon();
- static final class SharedLibraryEntry {
- final String path;
- final String apk;
+ public static final class SharedLibraryEntry {
+ public final String path;
+ public final String apk;
SharedLibraryEntry(String _path, String _apk) {
path = _path;
}
}
- // These are the built-in shared libraries that were read from the
- // etc/permissions.xml file.
- final HashMap<String, SharedLibraryEntry> mSharedLibraries
- = new HashMap<String, SharedLibraryEntry>();
-
- // These are the features this devices supports that were read from the
- // etc/permissions.xml file.
- final HashMap<String, FeatureInfo> mAvailableFeatures =
- new HashMap<String, FeatureInfo>();
-
- // If mac_permissions.xml was found for seinfo labeling.
- boolean mFoundPolicyFile;
-
- // If a recursive restorecon of /data/data/<pkg> is needed.
- private boolean mShouldRestoreconData = SELinuxMMAC.shouldRestorecon();
+ // Currently known shared libraries.
+ final HashMap<String, SharedLibraryEntry> mSharedLibraries =
+ new HashMap<String, SharedLibraryEntry>();
// All available activities, for your resolving pleasure.
final ActivityIntentResolver mActivities =
getDefaultDisplayMetrics(context, mMetrics);
+ SystemConfig systemConfig = SystemConfig.getInstance();
+ mGlobalGids = systemConfig.getGlobalGids();
+ mSystemPermissions = systemConfig.getSystemPermissions();
+ mAvailableFeatures = systemConfig.getAvailableFeatures();
+
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
sUserManager = new UserManagerService(context, this,
mInstallLock, mPackages);
- // Read permissions and features from system
- readPermissions(Environment.buildPath(
- Environment.getRootDirectory(), "etc", "permissions"), false);
- // Only read features from OEM
- readPermissions(Environment.buildPath(
- Environment.getOemDirectory(), "etc", "permissions"), true);
+ // Propagate permission configuration in to package manager.
+ ArrayMap<String, SystemConfig.PermissionEntry> permConfig
+ = systemConfig.getPermissions();
+ for (int i=0; i<permConfig.size(); i++) {
+ SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
+ BasePermission bp = mSettings.mPermissions.get(perm.name);
+ if (bp == null) {
+ bp = new BasePermission(perm.name, null, BasePermission.TYPE_BUILTIN);
+ mSettings.mPermissions.put(perm.name, bp);
+ }
+ if (perm.gids != null) {
+ bp.gids = appendInts(bp.gids, perm.gids);
+ }
+ }
+
+ ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
+ for (int i=0; i<libConfig.size(); i++) {
+ mSharedLibraries.put(libConfig.keyAt(i),
+ new SharedLibraryEntry(libConfig.valueAt(i), null));
+ }
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
mSettings.removePackageLPw(ps.name);
}
- void readPermissions(File libraryDir, boolean onlyFeatures) {
- // Read permissions from .../etc/permission directory.
- if (!libraryDir.exists() || !libraryDir.isDirectory()) {
- Slog.w(TAG, "No directory " + libraryDir + ", skipping");
- return;
- }
- if (!libraryDir.canRead()) {
- Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
- return;
- }
-
- // Iterate over the files in the directory and scan .xml files
- for (File f : libraryDir.listFiles()) {
- // We'll read platform.xml last
- if (f.getPath().endsWith("etc/permissions/platform.xml")) {
- continue;
- }
-
- if (!f.getPath().endsWith(".xml")) {
- Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
- continue;
- }
- if (!f.canRead()) {
- Slog.w(TAG, "Permissions library file " + f + " cannot be read");
- continue;
- }
-
- readPermissionsFromXml(f, onlyFeatures);
- }
-
- // Read permissions from .../etc/permissions/platform.xml last so it will take precedence
- final File permFile = new File(Environment.getRootDirectory(),
- "etc/permissions/platform.xml");
- readPermissionsFromXml(permFile, onlyFeatures);
- }
-
- private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
- FileReader permReader = null;
- try {
- permReader = new FileReader(permFile);
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
- return;
- }
-
- try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(permReader);
-
- XmlUtils.beginDocument(parser, "permissions");
-
- while (true) {
- XmlUtils.nextElement(parser);
- if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
- break;
- }
-
- String name = parser.getName();
- if ("group".equals(name) && !onlyFeatures) {
- String gidStr = parser.getAttributeValue(null, "gid");
- if (gidStr != null) {
- int gid = Process.getGidForName(gidStr);
- mGlobalGids = appendInt(mGlobalGids, gid);
- } else {
- Slog.w(TAG, "<group> without gid at "
- + parser.getPositionDescription());
- }
-
- XmlUtils.skipCurrentTag(parser);
- continue;
- } else if ("permission".equals(name) && !onlyFeatures) {
- String perm = parser.getAttributeValue(null, "name");
- if (perm == null) {
- Slog.w(TAG, "<permission> without name at "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- perm = perm.intern();
- readPermission(parser, perm);
-
- } else if ("assign-permission".equals(name) && !onlyFeatures) {
- String perm = parser.getAttributeValue(null, "name");
- if (perm == null) {
- Slog.w(TAG, "<assign-permission> without name at "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- String uidStr = parser.getAttributeValue(null, "uid");
- if (uidStr == null) {
- Slog.w(TAG, "<assign-permission> without uid at "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- int uid = Process.getUidForName(uidStr);
- if (uid < 0) {
- Slog.w(TAG, "<assign-permission> with unknown uid \""
- + uidStr + "\" at "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- perm = perm.intern();
- HashSet<String> perms = mSystemPermissions.get(uid);
- if (perms == null) {
- perms = new HashSet<String>();
- mSystemPermissions.put(uid, perms);
- }
- perms.add(perm);
- XmlUtils.skipCurrentTag(parser);
-
- } else if ("library".equals(name) && !onlyFeatures) {
- String lname = parser.getAttributeValue(null, "name");
- String lfile = parser.getAttributeValue(null, "file");
- if (lname == null) {
- Slog.w(TAG, "<library> without name at "
- + parser.getPositionDescription());
- } else if (lfile == null) {
- Slog.w(TAG, "<library> without file at "
- + parser.getPositionDescription());
- } else {
- //Log.i(TAG, "Got library " + lname + " in " + lfile);
- mSharedLibraries.put(lname, new SharedLibraryEntry(lfile, null));
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
-
- } else if ("feature".equals(name)) {
- String fname = parser.getAttributeValue(null, "name");
- if (fname == null) {
- Slog.w(TAG, "<feature> without name at "
- + parser.getPositionDescription());
- } else {
- //Log.i(TAG, "Got feature " + fname);
- FeatureInfo fi = new FeatureInfo();
- fi.name = fname;
- mAvailableFeatures.put(fname, fi);
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
-
- } else {
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
-
- }
- permReader.close();
- } catch (XmlPullParserException e) {
- Slog.w(TAG, "Got execption parsing permissions.", e);
- } catch (IOException e) {
- Slog.w(TAG, "Got execption parsing permissions.", e);
- }
- }
-
- void readPermission(XmlPullParser parser, String name)
- throws IOException, XmlPullParserException {
-
- name = name.intern();
-
- BasePermission bp = mSettings.mPermissions.get(name);
- if (bp == null) {
- bp = new BasePermission(name, null, BasePermission.TYPE_BUILTIN);
- mSettings.mPermissions.put(name, bp);
- }
- int outerDepth = parser.getDepth();
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if ("group".equals(tagName)) {
- String gidStr = parser.getAttributeValue(null, "gid");
- if (gidStr != null) {
- int gid = Process.getGidForName(gidStr);
- bp.gids = appendInt(bp.gids, gid);
- } else {
- Slog.w(TAG, "<group> without gid at "
- + parser.getPositionDescription());
- }
- }
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
static int[] appendInts(int[] cur, int[] add) {
if (add == null) return cur;
if (cur == null) return add;
boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
try {
+ Slog.i(TAG, "Reading configuration...");
+ SystemConfig.getInstance();
+
Slog.i(TAG, "Scheduling Policy");
ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());