Loading core/java/android/provider/Settings.java +9 −0 Original line number Diff line number Diff line Loading @@ -9308,6 +9308,15 @@ public final class Settings { public static final String NETWORK_METERED_MULTIPATH_PREFERENCE = "network_metered_multipath_preference"; /** * Default daily multipath budget used by ConnectivityManager.getMultipathPreference() * on metered networks. This default quota is only used if quota could not be determined * from data plan or data limit/warning set by the user. * @hide */ public static final String NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES = "network_default_daily_multipath_quota_bytes"; /** * Network watchlist last report time. * @hide Loading core/res/res/values/config.xml +7 −0 Original line number Diff line number Diff line Loading @@ -327,6 +327,13 @@ This is the default value of that setting. --> <integer translatable="false" name="config_networkMeteredMultipathPreference">0</integer> <!-- Default daily multipath budget used by ConnectivityManager.getMultipathPreference() on metered networks. This default quota only used if quota could not be determined from data plan or data limit/warning set by the user. The value that is actually used is controlled by Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES. This is the default value of that setting. --> <integer translatable="false" name="config_networkDefaultDailyMultipathQuotaBytes">2500000</integer> <!-- List of regexpressions describing the interface (if any) that represent tetherable USB interfaces. If the device doesn't want to support tethering over USB this should be empty. An example would be "usb.*" --> Loading core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -1922,6 +1922,7 @@ <java-symbol type="integer" name="config_networkWakeupPacketMask" /> <java-symbol type="bool" name="config_apfDrop802_3Frames" /> <java-symbol type="array" name="config_apfEthTypeBlackList" /> <java-symbol type="integer" name="config_networkDefaultDailyMultipathQuotaBytes" /> <java-symbol type="integer" name="config_networkMeteredMultipathPreference" /> <java-symbol type="integer" name="config_notificationsBatteryFullARGB" /> <java-symbol type="integer" name="config_notificationsBatteryLedOff" /> Loading core/tests/coretests/src/android/provider/SettingsBackupTest.java +1 −0 Original line number Diff line number Diff line Loading @@ -301,6 +301,7 @@ public class SettingsBackupTest { Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES, Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE, Settings.Global.NETWORK_AVOID_BAD_WIFI, Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES, Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE, Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME, Settings.Global.NETWORK_PREFERENCE, Loading services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java +82 −10 Original line number Diff line number Diff line Loading @@ -24,13 +24,19 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES; import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH; import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN; import android.app.usage.NetworkStatsManager; import android.app.usage.NetworkStatsManager.UsageCallback; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; Loading @@ -45,11 +51,15 @@ import android.net.StringNetworkSpecifier; import android.os.BestClock; import android.os.Handler; import android.os.SystemClock; import android.net.Uri; import android.os.UserHandle; import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.DebugUtils; import android.util.Pair; import android.util.Slog; import com.android.internal.R; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.net.NetworkPolicyManagerInternal; Loading @@ -60,7 +70,6 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.util.Calendar; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; Loading @@ -85,6 +94,9 @@ public class MultipathPolicyTracker { private final Handler mHandler; private final Clock mClock; private final Dependencies mDeps; private final ContentResolver mResolver; private final SettingsObserver mSettingsObserver; private final ConfigChangeReceiver mConfigChangeReceiver; private ConnectivityManager mCM; private NetworkPolicyManager mNPM; Loading @@ -93,8 +105,6 @@ public class MultipathPolicyTracker { private NetworkCallback mMobileNetworkCallback; private NetworkPolicyManager.Listener mPolicyListener; // STOPSHIP: replace this with a configurable mechanism. private static final long DEFAULT_DAILY_MULTIPATH_QUOTA = 2_500_000; /** * Divider to calculate opportunistic quota from user-set data limit or warning: 5% of user-set Loading @@ -118,6 +128,9 @@ public class MultipathPolicyTracker { mHandler = handler; mClock = deps.getClock(); mDeps = deps; mResolver = mContext.getContentResolver(); mSettingsObserver = new SettingsObserver(mHandler); mConfigChangeReceiver = new ConfigChangeReceiver(); // Because we are initialized by the ConnectivityService constructor, we can't touch any // connectivity APIs. Service initialization is done in start(). } Loading @@ -129,6 +142,14 @@ public class MultipathPolicyTracker { registerTrackMobileCallback(); registerNetworkPolicyListener(); final Uri defaultSettingUri = Settings.Global.getUriFor(NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES); mResolver.registerContentObserver(defaultSettingUri, false, mSettingsObserver); final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); mContext.registerReceiverAsUser( mConfigChangeReceiver, UserHandle.ALL, intentFilter, null, mHandler); } public void shutdown() { Loading @@ -138,6 +159,8 @@ public class MultipathPolicyTracker { t.shutdown(); } mMultipathTrackers.clear(); mResolver.unregisterContentObserver(mSettingsObserver); mContext.unregisterReceiver(mConfigChangeReceiver); } // Called on an arbitrary binder thread. Loading Loading @@ -292,11 +315,11 @@ public class MultipathPolicyTracker { // Fallback to user settings-based quota if not available from phone plan if (quota == OPPORTUNISTIC_QUOTA_UNKNOWN) { quota = getUserPolicyOpportunisticQuotaBytes(); if (DBG) Slog.d(TAG, "Opportunistic quota from user policy: " + quota + " bytes"); } if (quota == OPPORTUNISTIC_QUOTA_UNKNOWN) { // STOPSHIP: replace this with a configurable mechanism. quota = DEFAULT_DAILY_MULTIPATH_QUOTA; quota = getDefaultDailyMultipathQuotaBytes(); if (DBG) Slog.d(TAG, "Setting quota: " + quota + " bytes"); } Loading Loading @@ -374,6 +397,21 @@ public class MultipathPolicyTracker { private final ConcurrentHashMap <Network, MultipathTracker> mMultipathTrackers = new ConcurrentHashMap<>(); private long getDefaultDailyMultipathQuotaBytes() { final String setting = Settings.Global.getString(mContext.getContentResolver(), NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES); if (setting != null) { try { return Long.parseLong(setting); } catch(NumberFormatException e) { // fall through } } return mContext.getResources().getInteger( R.integer.config_networkDefaultDailyMultipathQuotaBytes); } // TODO: this races with app code that might respond to onAvailable() by immediately calling // getMultipathPreference. Fix this by adding to ConnectivityService the ability to directly // invoke NetworkCallbacks on tightly-coupled classes such as this one which run on its Loading Loading @@ -415,6 +453,15 @@ public class MultipathPolicyTracker { mCM.registerNetworkCallback(request, mMobileNetworkCallback, mHandler); } /** * Update multipath budgets for all trackers. To be called on the mHandler thread. */ private void updateAllMultipathBudgets() { for (MultipathTracker t : mMultipathTrackers.values()) { t.updateMultipathBudget(); } } private void maybeUnregisterTrackMobileCallback() { if (mMobileNetworkCallback != null) { mCM.unregisterNetworkCallback(mMobileNetworkCallback); Loading @@ -427,11 +474,7 @@ public class MultipathPolicyTracker { @Override public void onMeteredIfacesChanged(String[] meteredIfaces) { // Dispatched every time opportunistic quota is recalculated. mHandler.post(() -> { for (MultipathTracker t : mMultipathTrackers.values()) { t.updateMultipathBudget(); } }); mHandler.post(() -> updateAllMultipathBudgets()); } }; mNPM.registerListener(mPolicyListener); Loading @@ -441,6 +484,35 @@ public class MultipathPolicyTracker { mNPM.unregisterListener(mPolicyListener); } private final class SettingsObserver extends ContentObserver { public SettingsObserver(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange) { Slog.wtf(TAG, "Should never be reached."); } @Override public void onChange(boolean selfChange, Uri uri) { if (!Settings.Global.getUriFor(NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES) .equals(uri)) { Slog.wtf(TAG, "Unexpected settings observation: " + uri); } if (DBG) Slog.d(TAG, "Settings change: updating budgets."); updateAllMultipathBudgets(); } } private final class ConfigChangeReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (DBG) Slog.d(TAG, "Configuration change: updating budgets."); updateAllMultipathBudgets(); } } public void dump(IndentingPrintWriter pw) { // Do not use in production. Access to class data is only safe on the handler thrad. pw.println("MultipathPolicyTracker:"); Loading Loading
core/java/android/provider/Settings.java +9 −0 Original line number Diff line number Diff line Loading @@ -9308,6 +9308,15 @@ public final class Settings { public static final String NETWORK_METERED_MULTIPATH_PREFERENCE = "network_metered_multipath_preference"; /** * Default daily multipath budget used by ConnectivityManager.getMultipathPreference() * on metered networks. This default quota is only used if quota could not be determined * from data plan or data limit/warning set by the user. * @hide */ public static final String NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES = "network_default_daily_multipath_quota_bytes"; /** * Network watchlist last report time. * @hide Loading
core/res/res/values/config.xml +7 −0 Original line number Diff line number Diff line Loading @@ -327,6 +327,13 @@ This is the default value of that setting. --> <integer translatable="false" name="config_networkMeteredMultipathPreference">0</integer> <!-- Default daily multipath budget used by ConnectivityManager.getMultipathPreference() on metered networks. This default quota only used if quota could not be determined from data plan or data limit/warning set by the user. The value that is actually used is controlled by Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES. This is the default value of that setting. --> <integer translatable="false" name="config_networkDefaultDailyMultipathQuotaBytes">2500000</integer> <!-- List of regexpressions describing the interface (if any) that represent tetherable USB interfaces. If the device doesn't want to support tethering over USB this should be empty. An example would be "usb.*" --> Loading
core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -1922,6 +1922,7 @@ <java-symbol type="integer" name="config_networkWakeupPacketMask" /> <java-symbol type="bool" name="config_apfDrop802_3Frames" /> <java-symbol type="array" name="config_apfEthTypeBlackList" /> <java-symbol type="integer" name="config_networkDefaultDailyMultipathQuotaBytes" /> <java-symbol type="integer" name="config_networkMeteredMultipathPreference" /> <java-symbol type="integer" name="config_notificationsBatteryFullARGB" /> <java-symbol type="integer" name="config_notificationsBatteryLedOff" /> Loading
core/tests/coretests/src/android/provider/SettingsBackupTest.java +1 −0 Original line number Diff line number Diff line Loading @@ -301,6 +301,7 @@ public class SettingsBackupTest { Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES, Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE, Settings.Global.NETWORK_AVOID_BAD_WIFI, Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES, Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE, Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME, Settings.Global.NETWORK_PREFERENCE, Loading
services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java +82 −10 Original line number Diff line number Diff line Loading @@ -24,13 +24,19 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES; import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH; import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN; import android.app.usage.NetworkStatsManager; import android.app.usage.NetworkStatsManager.UsageCallback; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; Loading @@ -45,11 +51,15 @@ import android.net.StringNetworkSpecifier; import android.os.BestClock; import android.os.Handler; import android.os.SystemClock; import android.net.Uri; import android.os.UserHandle; import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.DebugUtils; import android.util.Pair; import android.util.Slog; import com.android.internal.R; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.net.NetworkPolicyManagerInternal; Loading @@ -60,7 +70,6 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.util.Calendar; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; Loading @@ -85,6 +94,9 @@ public class MultipathPolicyTracker { private final Handler mHandler; private final Clock mClock; private final Dependencies mDeps; private final ContentResolver mResolver; private final SettingsObserver mSettingsObserver; private final ConfigChangeReceiver mConfigChangeReceiver; private ConnectivityManager mCM; private NetworkPolicyManager mNPM; Loading @@ -93,8 +105,6 @@ public class MultipathPolicyTracker { private NetworkCallback mMobileNetworkCallback; private NetworkPolicyManager.Listener mPolicyListener; // STOPSHIP: replace this with a configurable mechanism. private static final long DEFAULT_DAILY_MULTIPATH_QUOTA = 2_500_000; /** * Divider to calculate opportunistic quota from user-set data limit or warning: 5% of user-set Loading @@ -118,6 +128,9 @@ public class MultipathPolicyTracker { mHandler = handler; mClock = deps.getClock(); mDeps = deps; mResolver = mContext.getContentResolver(); mSettingsObserver = new SettingsObserver(mHandler); mConfigChangeReceiver = new ConfigChangeReceiver(); // Because we are initialized by the ConnectivityService constructor, we can't touch any // connectivity APIs. Service initialization is done in start(). } Loading @@ -129,6 +142,14 @@ public class MultipathPolicyTracker { registerTrackMobileCallback(); registerNetworkPolicyListener(); final Uri defaultSettingUri = Settings.Global.getUriFor(NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES); mResolver.registerContentObserver(defaultSettingUri, false, mSettingsObserver); final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); mContext.registerReceiverAsUser( mConfigChangeReceiver, UserHandle.ALL, intentFilter, null, mHandler); } public void shutdown() { Loading @@ -138,6 +159,8 @@ public class MultipathPolicyTracker { t.shutdown(); } mMultipathTrackers.clear(); mResolver.unregisterContentObserver(mSettingsObserver); mContext.unregisterReceiver(mConfigChangeReceiver); } // Called on an arbitrary binder thread. Loading Loading @@ -292,11 +315,11 @@ public class MultipathPolicyTracker { // Fallback to user settings-based quota if not available from phone plan if (quota == OPPORTUNISTIC_QUOTA_UNKNOWN) { quota = getUserPolicyOpportunisticQuotaBytes(); if (DBG) Slog.d(TAG, "Opportunistic quota from user policy: " + quota + " bytes"); } if (quota == OPPORTUNISTIC_QUOTA_UNKNOWN) { // STOPSHIP: replace this with a configurable mechanism. quota = DEFAULT_DAILY_MULTIPATH_QUOTA; quota = getDefaultDailyMultipathQuotaBytes(); if (DBG) Slog.d(TAG, "Setting quota: " + quota + " bytes"); } Loading Loading @@ -374,6 +397,21 @@ public class MultipathPolicyTracker { private final ConcurrentHashMap <Network, MultipathTracker> mMultipathTrackers = new ConcurrentHashMap<>(); private long getDefaultDailyMultipathQuotaBytes() { final String setting = Settings.Global.getString(mContext.getContentResolver(), NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES); if (setting != null) { try { return Long.parseLong(setting); } catch(NumberFormatException e) { // fall through } } return mContext.getResources().getInteger( R.integer.config_networkDefaultDailyMultipathQuotaBytes); } // TODO: this races with app code that might respond to onAvailable() by immediately calling // getMultipathPreference. Fix this by adding to ConnectivityService the ability to directly // invoke NetworkCallbacks on tightly-coupled classes such as this one which run on its Loading Loading @@ -415,6 +453,15 @@ public class MultipathPolicyTracker { mCM.registerNetworkCallback(request, mMobileNetworkCallback, mHandler); } /** * Update multipath budgets for all trackers. To be called on the mHandler thread. */ private void updateAllMultipathBudgets() { for (MultipathTracker t : mMultipathTrackers.values()) { t.updateMultipathBudget(); } } private void maybeUnregisterTrackMobileCallback() { if (mMobileNetworkCallback != null) { mCM.unregisterNetworkCallback(mMobileNetworkCallback); Loading @@ -427,11 +474,7 @@ public class MultipathPolicyTracker { @Override public void onMeteredIfacesChanged(String[] meteredIfaces) { // Dispatched every time opportunistic quota is recalculated. mHandler.post(() -> { for (MultipathTracker t : mMultipathTrackers.values()) { t.updateMultipathBudget(); } }); mHandler.post(() -> updateAllMultipathBudgets()); } }; mNPM.registerListener(mPolicyListener); Loading @@ -441,6 +484,35 @@ public class MultipathPolicyTracker { mNPM.unregisterListener(mPolicyListener); } private final class SettingsObserver extends ContentObserver { public SettingsObserver(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange) { Slog.wtf(TAG, "Should never be reached."); } @Override public void onChange(boolean selfChange, Uri uri) { if (!Settings.Global.getUriFor(NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES) .equals(uri)) { Slog.wtf(TAG, "Unexpected settings observation: " + uri); } if (DBG) Slog.d(TAG, "Settings change: updating budgets."); updateAllMultipathBudgets(); } } private final class ConfigChangeReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (DBG) Slog.d(TAG, "Configuration change: updating budgets."); updateAllMultipathBudgets(); } } public void dump(IndentingPrintWriter pw) { // Do not use in production. Access to class data is only safe on the handler thrad. pw.println("MultipathPolicyTracker:"); Loading