/* * Copyright (C) 2011 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.net; import static android.Manifest.permission.ACCESS_NETWORK_STATE; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.MANAGE_NETWORK_POLICY; import static android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS; import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.Manifest.permission.READ_PHONE_STATE; import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; import static android.content.Intent.ACTION_PACKAGE_ADDED; import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.ACTION_USER_ADDED; import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.EXTRA_UID; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS; import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS; import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED; import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground; import static android.net.NetworkPolicyManager.resolveNetworkId; import static android.net.NetworkPolicyManager.uidPoliciesToString; import static android.net.NetworkPolicyManager.uidRulesToString; import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER; import static android.net.NetworkTemplate.MATCH_MOBILE_4G; import static android.net.NetworkTemplate.MATCH_MOBILE_ALL; import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.TrafficStats.MB_IN_BYTES; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED; import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static com.android.internal.util.ArrayUtils.appendInt; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readIntAttribute; import static com.android.internal.util.XmlUtils.readLongAttribute; import static com.android.internal.util.XmlUtils.readStringAttribute; import static com.android.internal.util.XmlUtils.writeBooleanAttribute; import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.internal.util.XmlUtils.writeStringAttribute; import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT; import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; import static org.xmlpull.v1.XmlPullParser.START_TAG; import android.Manifest; import android.annotation.IntDef; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.INotificationManager; import android.app.IUidObserver; import android.app.Notification; import android.app.PendingIntent; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.IConnectivityManager; import android.net.INetworkManagementEventObserver; import android.net.INetworkPolicyListener; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkIdentity; import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkState; import android.net.NetworkTemplate; import android.net.TrafficStats; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.Binder; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.IDeviceIdleController; import android.os.INetworkManagementService; import android.os.Message; import android.os.MessageQueue.IdleHandler; import android.os.PersistableBundle; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.ShellCallback; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.provider.Settings.Global; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.SubscriptionPlan; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.text.format.Formatter; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Log; import android.util.NtpTrustedTime; import android.util.Pair; import android.util.RecurrenceRule; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.TrustedTime; import android.util.Xml; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.telephony.PhoneConstants; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.server.DeviceIdleController; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemConfig; import com.android.server.power.BatterySaverPolicy.ServiceType; import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Service that maintains low-level network policy rules, using * {@link NetworkStatsService} statistics to drive those rules. *
* Derives active rules by combining a given policy with other system status, * and delivers to listeners, such as {@link ConnectivityManager}, for * enforcement. * *
* This class uses 2-3 locks to synchronize state: *
* As such, methods that require synchronization have the following prefixes: *
* Useful for the cases where the lack of network access can simplify the rules. */ private boolean hasInternetPermissions(int uid) { try { if (mIPm.checkUidPermission(Manifest.permission.INTERNET, uid) != PackageManager.PERMISSION_GRANTED) { return false; } } catch (RemoteException e) { } return true; } /** * Clears all state - internal and external - associated with an UID. */ private void onUidDeletedUL(int uid) { // First cleanup in-memory state synchronously... mUidRules.delete(uid); mUidPolicy.delete(uid); mUidFirewallStandbyRules.delete(uid); mUidFirewallDozableRules.delete(uid); mUidFirewallPowerSaveRules.delete(uid); mPowerSaveWhitelistExceptIdleAppIds.delete(uid); mPowerSaveWhitelistAppIds.delete(uid); mPowerSaveTempWhitelistAppIds.delete(uid); // ...then update iptables asynchronously. mHandler.obtainMessage(MSG_RESET_FIREWALL_RULES_BY_UID, uid, 0).sendToTarget(); } /** * Applies network rules to bandwidth and firewall controllers based on uid policy. * *
There are currently 4 types of restriction rules: *
This method changes both the external firewall rules and the internal state. */ private void updateRestrictionRulesForUidUL(int uid) { // Methods below only changes the firewall rules for the power-related modes. updateRuleForDeviceIdleUL(uid); updateRuleForAppIdleUL(uid); updateRuleForRestrictPowerUL(uid); // Update internal state for power-related modes. updateRulesForPowerRestrictionsUL(uid); // Update firewall and internal rules for Data Saver Mode. updateRulesForDataUsageRestrictionsUL(uid); } /** * Applies network rules to bandwidth controllers based on process state and user-defined * restrictions (blacklist / whitelist). * *
* {@code netd} defines 3 firewall chains that govern whether an app has access to metered * networks: *
The @{code bw_penalty_box} and @{code bw_happy_box} are primarily managed through the * {@link #setUidPolicy(int, int)} and {@link #addRestrictBackgroundWhitelistedUid(int)} / * {@link #removeRestrictBackgroundWhitelistedUid(int)} methods (for blacklist and whitelist * respectively): these methods set the proper internal state (blacklist / whitelist), then call * this ({@link #updateRulesForDataUsageRestrictionsUL(int)}) to propagate the rules to * {@link INetworkManagementService}, but this method should also be called in events (like * Data Saver Mode flips or UID state changes) that might affect the foreground app, since the * following rules should also be applied: * *
For optimization, the rules are only applied on user apps that have internet access * permission, since there is no need to change the {@code iptables} rule if the app does not * have permission to use the internet. * *
The {@link #mUidRules} map is used to define the transtion of states of an UID. * */ private void updateRulesForDataUsageRestrictionsUL(int uid) { if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForDataUsageRestrictionsUL: " + uid); } try { updateRulesForDataUsageRestrictionsULInner(uid); } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } } private void updateRulesForDataUsageRestrictionsULInner(int uid) { if (!isUidValidForWhitelistRules(uid)) { if (LOGD) Slog.d(TAG, "no need to update restrict data rules for uid " + uid); return; } final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE); final int oldUidRules = mUidRules.get(uid, RULE_NONE); final boolean isForeground = isUidForegroundOnRestrictBackgroundUL(uid); final boolean isBlacklisted = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0; final boolean isWhitelisted = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0; final int oldRule = oldUidRules & MASK_METERED_NETWORKS; int newRule = RULE_NONE; // First step: define the new rule based on user restrictions and foreground state. if (isForeground) { if (isBlacklisted || (mRestrictBackground && !isWhitelisted)) { newRule = RULE_TEMPORARY_ALLOW_METERED; } else if (isWhitelisted) { newRule = RULE_ALLOW_METERED; } } else { if (isBlacklisted) { newRule = RULE_REJECT_METERED; } else if (mRestrictBackground && isWhitelisted) { newRule = RULE_ALLOW_METERED; } } final int newUidRules = newRule | (oldUidRules & MASK_ALL_NETWORKS); if (LOGV) { Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")" + ": isForeground=" +isForeground + ", isBlacklisted=" + isBlacklisted + ", isWhitelisted=" + isWhitelisted + ", oldRule=" + uidRulesToString(oldRule) + ", newRule=" + uidRulesToString(newRule) + ", newUidRules=" + uidRulesToString(newUidRules) + ", oldUidRules=" + uidRulesToString(oldUidRules)); } if (newUidRules == RULE_NONE) { mUidRules.delete(uid); } else { mUidRules.put(uid, newUidRules); } // Second step: apply bw changes based on change of state. if (newRule != oldRule) { if (hasRule(newRule, RULE_TEMPORARY_ALLOW_METERED)) { // Temporarily whitelist foreground app, removing from blacklist if necessary // (since bw_penalty_box prevails over bw_happy_box). setMeteredNetworkWhitelist(uid, true); // TODO: if statement below is used to avoid an unnecessary call to netd / iptables, // but ideally it should be just: // setMeteredNetworkBlacklist(uid, isBlacklisted); if (isBlacklisted) { setMeteredNetworkBlacklist(uid, false); } } else if (hasRule(oldRule, RULE_TEMPORARY_ALLOW_METERED)) { // Remove temporary whitelist from app that is not on foreground anymore. // TODO: if statements below are used to avoid unnecessary calls to netd / iptables, // but ideally they should be just: // setMeteredNetworkWhitelist(uid, isWhitelisted); // setMeteredNetworkBlacklist(uid, isBlacklisted); if (!isWhitelisted) { setMeteredNetworkWhitelist(uid, false); } if (isBlacklisted) { setMeteredNetworkBlacklist(uid, true); } } else if (hasRule(newRule, RULE_REJECT_METERED) || hasRule(oldRule, RULE_REJECT_METERED)) { // Flip state because app was explicitly added or removed to blacklist. setMeteredNetworkBlacklist(uid, isBlacklisted); if (hasRule(oldRule, RULE_REJECT_METERED) && isWhitelisted) { // Since blacklist prevails over whitelist, we need to handle the special case // where app is whitelisted and blacklisted at the same time (although such // scenario should be blocked by the UI), then blacklist is removed. setMeteredNetworkWhitelist(uid, isWhitelisted); } } else if (hasRule(newRule, RULE_ALLOW_METERED) || hasRule(oldRule, RULE_ALLOW_METERED)) { // Flip state because app was explicitly added or removed to whitelist. setMeteredNetworkWhitelist(uid, isWhitelisted); } else { // All scenarios should have been covered above. Log.wtf(TAG, "Unexpected change of metered UID state for " + uid + ": foreground=" + isForeground + ", whitelisted=" + isWhitelisted + ", blacklisted=" + isBlacklisted + ", newRule=" + uidRulesToString(newUidRules) + ", oldRule=" + uidRulesToString(oldUidRules)); } // Dispatch changed rule to existing listeners. mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget(); } } /** * Updates the power-related part of the {@link #mUidRules} for a given map, and notify external * listeners in case of change. *
* There are 3 power-related rules that affects whether an app has background access on * non-metered networks, and when the condition applies and the UID is not whitelisted for power * restriction, it's added to the equivalent firewall chain: *
* This method updates the power-related part of the {@link #mUidRules} for a given uid based on * these modes, the UID process state (foreground or not), and the UIDwhitelist state. *
* NOTE: This method does not update the firewall rules on {@code netd}. */ private void updateRulesForPowerRestrictionsUL(int uid) { final int oldUidRules = mUidRules.get(uid, RULE_NONE); final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules, false); if (newUidRules == RULE_NONE) { mUidRules.delete(uid); } else { mUidRules.put(uid, newUidRules); } } /** * Similar to above but ignores idle state if app standby is currently disabled by parole. * * @param uid the uid of the app to update rules for * @param oldUidRules the current rules for the uid, in order to determine if there's a change * @param paroled whether to ignore idle state of apps and only look at other restrictions. * * @return the new computed rules for the uid */ private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean paroled) { if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules + "/" + (paroled ? "P" : "-")); } try { return updateRulesForPowerRestrictionsULInner(uid, oldUidRules, paroled); } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } } private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules, boolean paroled) { if (!isUidValidForBlacklistRules(uid)) { if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid); return RULE_NONE; } final boolean isIdle = !paroled && isUidIdle(uid); final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode; final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid); final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid, mDeviceIdleMode); final int oldRule = oldUidRules & MASK_ALL_NETWORKS; int newRule = RULE_NONE; // First step: define the new rule based on user restrictions and foreground state. // NOTE: if statements below could be inlined, but it's easier to understand the logic // by considering the foreground and non-foreground states. if (isForeground) { if (restrictMode) { newRule = RULE_ALLOW_ALL; } } else if (restrictMode) { newRule = isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL; } final int newUidRules = (oldUidRules & MASK_METERED_NETWORKS) | newRule; if (LOGV) { Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")" + ", isIdle: " + isIdle + ", mRestrictPower: " + mRestrictPower + ", mDeviceIdleMode: " + mDeviceIdleMode + ", isForeground=" + isForeground + ", isWhitelisted=" + isWhitelisted + ", oldRule=" + uidRulesToString(oldRule) + ", newRule=" + uidRulesToString(newRule) + ", newUidRules=" + uidRulesToString(newUidRules) + ", oldUidRules=" + uidRulesToString(oldUidRules)); } // Second step: notify listeners if state changed. if (newRule != oldRule) { if (newRule == RULE_NONE || hasRule(newRule, RULE_ALLOW_ALL)) { if (LOGV) Log.v(TAG, "Allowing non-metered access for UID " + uid); } else if (hasRule(newRule, RULE_REJECT_ALL)) { if (LOGV) Log.v(TAG, "Rejecting non-metered access for UID " + uid); } else { // All scenarios should have been covered above Log.wtf(TAG, "Unexpected change of non-metered UID state for " + uid + ": foreground=" + isForeground + ", whitelisted=" + isWhitelisted + ", newRule=" + uidRulesToString(newUidRules) + ", oldRule=" + uidRulesToString(oldUidRules)); } mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget(); } return newUidRules; } private class AppIdleStateChangeListener extends UsageStatsManagerInternal.AppIdleStateChangeListener { @Override public void onAppIdleStateChanged(String packageName, int userId, boolean idle) { try { final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); if (LOGV) Log.v(TAG, "onAppIdleStateChanged(): uid=" + uid + ", idle=" + idle); synchronized (mUidRulesFirstLock) { updateRuleForAppIdleUL(uid); updateRulesForPowerRestrictionsUL(uid); } } catch (NameNotFoundException nnfe) { } } @Override public void onParoleStateChanged(boolean isParoleOn) { synchronized (mUidRulesFirstLock) { updateRulesForAppIdleParoleUL(); } } } private void dispatchUidRulesChanged(INetworkPolicyListener listener, int uid, int uidRules) { if (listener != null) { try { listener.onUidRulesChanged(uid, uidRules); } catch (RemoteException ignored) { } } } private void dispatchMeteredIfacesChanged(INetworkPolicyListener listener, String[] meteredIfaces) { if (listener != null) { try { listener.onMeteredIfacesChanged(meteredIfaces); } catch (RemoteException ignored) { } } } private void dispatchRestrictBackgroundChanged(INetworkPolicyListener listener, boolean restrictBackground) { if (listener != null) { try { listener.onRestrictBackgroundChanged(restrictBackground); } catch (RemoteException ignored) { } } } private void dispatchUidPoliciesChanged(INetworkPolicyListener listener, int uid, int uidPolicies) { if (listener != null) { try { listener.onUidPoliciesChanged(uid, uidPolicies); } catch (RemoteException ignored) { } } } private final Handler.Callback mHandlerCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_RULES_CHANGED: { final int uid = msg.arg1; final int uidRules = msg.arg2; final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); dispatchUidRulesChanged(listener, uid, uidRules); } mListeners.finishBroadcast(); return true; } case MSG_METERED_IFACES_CHANGED: { final String[] meteredIfaces = (String[]) msg.obj; final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); dispatchMeteredIfacesChanged(listener, meteredIfaces); } mListeners.finishBroadcast(); return true; } case MSG_LIMIT_REACHED: { final String iface = (String) msg.obj; maybeRefreshTrustedTime(); synchronized (mNetworkPoliciesSecondLock) { if (mMeteredIfaces.contains(iface)) { try { // force stats update to make sure we have // numbers that caused alert to trigger. mNetworkStats.forceUpdate(); } catch (RemoteException e) { // ignored; service lives in system_server } updateNetworkEnabledNL(); updateNotificationsNL(); } } return true; } case MSG_RESTRICT_BACKGROUND_CHANGED: { final boolean restrictBackground = msg.arg1 != 0; final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); dispatchRestrictBackgroundChanged(listener, restrictBackground); } mListeners.finishBroadcast(); final Intent intent = new Intent(ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED); intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mContext.sendBroadcastAsUser(intent, UserHandle.ALL); return true; } case MSG_POLICIES_CHANGED: { final int uid = msg.arg1; final int policy = msg.arg2; final Boolean notifyApp = (Boolean) msg.obj; // First notify internal listeners... final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); dispatchUidPoliciesChanged(listener, uid, policy); } mListeners.finishBroadcast(); // ...then apps listening to ACTION_RESTRICT_BACKGROUND_CHANGED if (notifyApp.booleanValue()) { broadcastRestrictBackgroundChanged(uid, notifyApp); } return true; } case MSG_ADVISE_PERSIST_THRESHOLD: { final long lowestRule = (Long) msg.obj; try { // make sure stats are recorded frequently enough; we aim // for 2MB threshold for 2GB/month rules. final long persistThreshold = lowestRule / 1000; mNetworkStats.advisePersistThreshold(persistThreshold); } catch (RemoteException e) { // ignored; service lives in system_server } return true; } case MSG_UPDATE_INTERFACE_QUOTA: { removeInterfaceQuota((String) msg.obj); // int params need to be stitched back into a long setInterfaceQuota((String) msg.obj, ((long) msg.arg1 << 32) | (msg.arg2 & 0xFFFFFFFFL)); return true; } case MSG_REMOVE_INTERFACE_QUOTA: { removeInterfaceQuota((String) msg.obj); return true; } case MSG_RESET_FIREWALL_RULES_BY_UID: { resetUidFirewallRules(msg.arg1); return true; } default: { return false; } } } }; private final Handler.Callback mUidEventHandlerCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { switch (msg.what) { case UID_MSG_STATE_CHANGED: { final int uid = msg.arg1; final int procState = msg.arg2; final long procStateSeq = (Long) msg.obj; handleUidChanged(uid, procState, procStateSeq); return true; } case UID_MSG_GONE: { final int uid = msg.arg1; handleUidGone(uid); return true; } default: { return false; } } } }; void handleUidChanged(int uid, int procState, long procStateSeq) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidStateChanged"); try { synchronized (mUidRulesFirstLock) { // We received a uid state change callback, add it to the history so that it // will be useful for debugging. mObservedHistory.addProcStateSeqUL(uid, procStateSeq); // Now update the network policy rules as per the updated uid state. updateUidStateUL(uid, procState); // Updating the network rules is done, so notify AMS about this. mActivityManagerInternal.notifyNetworkPolicyRulesUpdated(uid, procStateSeq); } } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } } void handleUidGone(int uid) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidGone"); try { synchronized (mUidRulesFirstLock) { removeUidStateUL(uid); } } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } } private void broadcastRestrictBackgroundChanged(int uid, Boolean changed) { final PackageManager pm = mContext.getPackageManager(); final String[] packages = pm.getPackagesForUid(uid); if (packages != null) { final int userId = UserHandle.getUserId(uid); for (String packageName : packages) { final Intent intent = new Intent(ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED); intent.setPackage(packageName); intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); } } } private void setInterfaceQuotaAsync(String iface, long quotaBytes) { // long quotaBytes split up into two ints to fit in message mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTA, (int) (quotaBytes >> 32), (int) (quotaBytes & 0xFFFFFFFF), iface).sendToTarget(); } private void setInterfaceQuota(String iface, long quotaBytes) { try { mNetworkManager.setInterfaceQuota(iface, quotaBytes); } catch (IllegalStateException e) { Log.wtf(TAG, "problem setting interface quota", e); } catch (RemoteException e) { // ignored; service lives in system_server } } private void removeInterfaceQuotaAsync(String iface) { mHandler.obtainMessage(MSG_REMOVE_INTERFACE_QUOTA, iface).sendToTarget(); } private void removeInterfaceQuota(String iface) { try { mNetworkManager.removeInterfaceQuota(iface); } catch (IllegalStateException e) { Log.wtf(TAG, "problem removing interface quota", e); } catch (RemoteException e) { // ignored; service lives in system_server } } private void setMeteredNetworkBlacklist(int uid, boolean enable) { if (LOGV) Slog.v(TAG, "setMeteredNetworkBlacklist " + uid + ": " + enable); try { mNetworkManager.setUidMeteredNetworkBlacklist(uid, enable); } catch (IllegalStateException e) { Log.wtf(TAG, "problem setting blacklist (" + enable + ") rules for " + uid, e); } catch (RemoteException e) { // ignored; service lives in system_server } } private void setMeteredNetworkWhitelist(int uid, boolean enable) { if (LOGV) Slog.v(TAG, "setMeteredNetworkWhitelist " + uid + ": " + enable); try { mNetworkManager.setUidMeteredNetworkWhitelist(uid, enable); } catch (IllegalStateException e) { Log.wtf(TAG, "problem setting whitelist (" + enable + ") rules for " + uid, e); } catch (RemoteException e) { // ignored; service lives in system_server } } private static final int CHAIN_TOGGLE_NONE = 0; private static final int CHAIN_TOGGLE_ENABLE = 1; private static final int CHAIN_TOGGLE_DISABLE = 2; @Retention(RetentionPolicy.SOURCE) @IntDef(flag = false, value = { CHAIN_TOGGLE_NONE, CHAIN_TOGGLE_ENABLE, CHAIN_TOGGLE_DISABLE }) public @interface ChainToggleType { } /** * Calls {@link #setUidFirewallRules(int, SparseIntArray)} and * {@link #enableFirewallChainUL(int, boolean)} synchronously. * * @param chain firewall chain. * @param uidRules new UID rules; if {@code null}, only toggles chain state. * @param toggle whether the chain should be enabled, disabled, or not changed. */ private void setUidFirewallRulesUL(int chain, @Nullable SparseIntArray uidRules, @ChainToggleType int toggle) { if (uidRules != null) { setUidFirewallRulesUL(chain, uidRules); } if (toggle != CHAIN_TOGGLE_NONE) { enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE); } } /** * Set uid rules on a particular firewall chain. This is going to synchronize the rules given * here to netd. It will clean up dead rules and make sure the target chain only contains rules * specified here. */ private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) { try { int size = uidRules.size(); int[] uids = new int[size]; int[] rules = new int[size]; for(int index = size - 1; index >= 0; --index) { uids[index] = uidRules.keyAt(index); rules[index] = uidRules.valueAt(index); } mNetworkManager.setFirewallUidRules(chain, uids, rules); } catch (IllegalStateException e) { Log.wtf(TAG, "problem setting firewall uid rules", e); } catch (RemoteException e) { // ignored; service lives in system_server } } /** * Add or remove a uid to the firewall blacklist for all network ifaces. */ private void setUidFirewallRule(int chain, int uid, int rule) { if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setUidFirewallRule: " + chain + "/" + uid + "/" + rule); } try { if (chain == FIREWALL_CHAIN_DOZABLE) { mUidFirewallDozableRules.put(uid, rule); } else if (chain == FIREWALL_CHAIN_STANDBY) { mUidFirewallStandbyRules.put(uid, rule); } else if (chain == FIREWALL_CHAIN_POWERSAVE) { mUidFirewallPowerSaveRules.put(uid, rule); } try { mNetworkManager.setFirewallUidRule(chain, uid, rule); } catch (IllegalStateException e) { Log.wtf(TAG, "problem setting firewall uid rules", e); } catch (RemoteException e) { // ignored; service lives in system_server } } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } } /** * Add or remove a uid to the firewall blacklist for all network ifaces. */ private void enableFirewallChainUL(int chain, boolean enable) { if (mFirewallChainStates.indexOfKey(chain) >= 0 && mFirewallChainStates.get(chain) == enable) { // All is the same, nothing to do. return; } mFirewallChainStates.put(chain, enable); try { mNetworkManager.setFirewallChainEnabled(chain, enable); } catch (IllegalStateException e) { Log.wtf(TAG, "problem enable firewall chain", e); } catch (RemoteException e) { // ignored; service lives in system_server } } /** * Resets all firewall rules associated with an UID. */ private void resetUidFirewallRules(int uid) { try { mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_DEFAULT); mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT); mNetworkManager .setFirewallUidRule(FIREWALL_CHAIN_POWERSAVE, uid, FIREWALL_RULE_DEFAULT); mNetworkManager.setUidMeteredNetworkWhitelist(uid, false); mNetworkManager.setUidMeteredNetworkBlacklist(uid, false); } catch (IllegalStateException e) { Log.wtf(TAG, "problem resetting firewall uid rules for " + uid, e); } catch (RemoteException e) { // ignored; service lives in system_server } } private long getTotalBytes(NetworkTemplate template, long start, long end) { try { return mNetworkStats.getNetworkTotalBytes(template, start, end); } catch (RuntimeException e) { Slog.w(TAG, "problem reading network stats: " + e); return 0; } catch (RemoteException e) { // ignored; service lives in system_server return 0; } } private boolean isBandwidthControlEnabled() { final long token = Binder.clearCallingIdentity(); try { return mNetworkManager.isBandwidthControlEnabled(); } catch (RemoteException e) { // ignored; service lives in system_server return false; } finally { Binder.restoreCallingIdentity(token); } } /** * Try refreshing {@link #mTime} when stale. */ void maybeRefreshTrustedTime() { if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) { mTime.forceRefresh(); } } private long currentTimeMillis() { return mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis(); } private static Intent buildAllowBackgroundDataIntent() { return new Intent(ACTION_ALLOW_BACKGROUND); } private static Intent buildSnoozeWarningIntent(NetworkTemplate template) { final Intent intent = new Intent(ACTION_SNOOZE_WARNING); intent.putExtra(EXTRA_NETWORK_TEMPLATE, template); return intent; } private static Intent buildNetworkOverLimitIntent(Resources res, NetworkTemplate template) { final Intent intent = new Intent(); intent.setComponent(ComponentName.unflattenFromString( res.getString(R.string.config_networkOverLimitComponent))); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(EXTRA_NETWORK_TEMPLATE, template); return intent; } private static Intent buildViewDataUsageIntent(Resources res, NetworkTemplate template) { final Intent intent = new Intent(); intent.setComponent(ComponentName.unflattenFromString( res.getString(R.string.config_dataUsageSummaryComponent))); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(EXTRA_NETWORK_TEMPLATE, template); return intent; } @VisibleForTesting public void addIdleHandler(IdleHandler handler) { mHandler.getLooper().getQueue().addIdleHandler(handler); } @VisibleForTesting public void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) { mRestrictBackgroundPowerState = result; boolean restrictBackground = result.batterySaverEnabled; boolean shouldInvokeRestrictBackground; // store the temporary mRestrictBackgroundChangedInBsm and update it at last boolean localRestrictBgChangedInBsm = mRestrictBackgroundChangedInBsm; if (result.globalBatterySaverEnabled) { // Try to turn on restrictBackground if (1) it is off and (2) batter saver need to // turn it on. shouldInvokeRestrictBackground = !mRestrictBackground && result.batterySaverEnabled; mRestrictBackgroundBeforeBsm = mRestrictBackground; localRestrictBgChangedInBsm = false; } else { // Try to restore the restrictBackground if it doesn't change in bsm shouldInvokeRestrictBackground = !mRestrictBackgroundChangedInBsm; restrictBackground = mRestrictBackgroundBeforeBsm; } if (shouldInvokeRestrictBackground) { setRestrictBackground(restrictBackground); } // Change it at last so setRestrictBackground() won't affect this variable mRestrictBackgroundChangedInBsm = localRestrictBgChangedInBsm; } private static void collectKeys(SparseIntArray source, SparseBooleanArray target) { final int size = source.size(); for (int i = 0; i < size; i++) { target.put(source.keyAt(i), true); } } @Override public void factoryReset(String subscriber) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) { return; } // Turn mobile data limit off NetworkPolicy[] policies = getNetworkPolicies(mContext.getOpPackageName()); NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriber); for (NetworkPolicy policy : policies) { if (policy.template.equals(template)) { policy.limitBytes = NetworkPolicy.LIMIT_DISABLED; policy.inferred = false; policy.clearSnooze(); } } setNetworkPolicies(policies); // Turn restrict background data off setRestrictBackground(false); if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL)) { // Remove app's "restrict background data" flag for (int uid : getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND)) { setUidPolicy(uid, POLICY_NONE); } } } @Override public boolean isUidNetworkingBlocked(int uid, boolean isNetworkMetered) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); return isUidNetworkingBlockedInternal(uid, isNetworkMetered); } private boolean isUidNetworkingBlockedInternal(int uid, boolean isNetworkMetered) { final int uidRules; final boolean isBackgroundRestricted; synchronized (mUidRulesFirstLock) { uidRules = mUidRules.get(uid, RULE_NONE); isBackgroundRestricted = mRestrictBackground; } if (hasRule(uidRules, RULE_REJECT_ALL)) { if (LOGV) logUidStatus(uid, "blocked by power restrictions"); return true; } if (!isNetworkMetered) { if (LOGV) logUidStatus(uid, "allowed on unmetered network"); return false; } if (hasRule(uidRules, RULE_REJECT_METERED)) { if (LOGV) logUidStatus(uid, "blacklisted on metered network"); return true; } if (hasRule(uidRules, RULE_ALLOW_METERED)) { if (LOGV) logUidStatus(uid, "whitelisted on metered network"); return false; } if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) { if (LOGV) logUidStatus(uid, "temporary whitelisted on metered network"); return false; } if (isBackgroundRestricted) { if (LOGV) logUidStatus(uid, "blocked when background is restricted"); return true; } if (LOGV) logUidStatus(uid, "allowed by default"); return false; } private class NetworkPolicyManagerInternalImpl extends NetworkPolicyManagerInternal { @Override public void resetUserState(int userId) { synchronized (mUidRulesFirstLock) { boolean changed = removeUserStateUL(userId, false); changed = addDefaultRestrictBackgroundWhitelistUidsUL(userId) || changed; if (changed) { synchronized (mNetworkPoliciesSecondLock) { writePolicyAL(); } } } } /** * @return true if the given uid is restricted from doing networking on metered networks. */ @Override public boolean isUidRestrictedOnMeteredNetworks(int uid) { final int uidRules; final boolean isBackgroundRestricted; synchronized (mUidRulesFirstLock) { uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); isBackgroundRestricted = mRestrictBackground; } return isBackgroundRestricted && !hasRule(uidRules, RULE_ALLOW_METERED) && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED); } /** * @return true if networking is blocked on the given interface for the given uid according * to current networking policies. */ @Override public boolean isUidNetworkingBlocked(int uid, String ifname) { final boolean isNetworkMetered; synchronized (mNetworkPoliciesSecondLock) { isNetworkMetered = mMeteredIfaces.contains(ifname); } return isUidNetworkingBlockedInternal(uid, isNetworkMetered); } } private static boolean hasRule(int uidRules, int rule) { return (uidRules & rule) != 0; } private static void logUidStatus(int uid, String descr) { Slog.d(TAG, String.format("uid %d is %s", uid, descr)); } /** * This class is used for storing and dumping the last {@link #MAX_PROC_STATE_SEQ_HISTORY} * (uid, procStateSeq) pairs. */ @VisibleForTesting public static final class ProcStateSeqHistory { private static final int INVALID_UID = -1; /** * Denotes maximum number of items this history can hold. */ private final int mMaxCapacity; /** * Used for storing the uid information. */ private final int[] mUids; /** * Used for storing the sequence numbers associated with {@link #mUids}. */ private final long[] mProcStateSeqs; /** * Points to the next available slot for writing (uid, procStateSeq) pair. */ private int mHistoryNext; public ProcStateSeqHistory(int maxCapacity) { mMaxCapacity = maxCapacity; mUids = new int[mMaxCapacity]; Arrays.fill(mUids, INVALID_UID); mProcStateSeqs = new long[mMaxCapacity]; } @GuardedBy("mUidRulesFirstLock") public void addProcStateSeqUL(int uid, long procStateSeq) { mUids[mHistoryNext] = uid; mProcStateSeqs[mHistoryNext] = procStateSeq; mHistoryNext = increaseNext(mHistoryNext, 1); } @GuardedBy("mUidRulesFirstLock") public void dumpUL(IndentingPrintWriter fout) { if (mUids[0] == INVALID_UID) { fout.println("NONE"); return; } int index = mHistoryNext; do { index = increaseNext(index, -1); if (mUids[index] == INVALID_UID) { break; } fout.println(getString(mUids[index], mProcStateSeqs[index])); } while (index != mHistoryNext); } public static String getString(int uid, long procStateSeq) { return "UID=" + uid + " Seq=" + procStateSeq; } private int increaseNext(int next, int increment) { next += increment; if (next >= mMaxCapacity) { next = 0; } else if (next < 0) { next = mMaxCapacity - 1; } return next; } } private class NotificationId { private final String mTag; private final int mId; NotificationId(NetworkPolicy policy, int type) { mTag = buildNotificationTag(policy, type); mId = type; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof NotificationId)) return false; NotificationId that = (NotificationId) o; return Objects.equals(mTag, that.mTag); } @Override public int hashCode() { return Objects.hash(mTag); } /** * Build unique tag that identifies an active {@link NetworkPolicy} * notification of a specific type, like {@link #TYPE_LIMIT}. */ private String buildNotificationTag(NetworkPolicy policy, int type) { return TAG + ":" + policy.template.hashCode() + ":" + type; } public String getTag() { return mTag; } public int getId() { return mId; } } }