Loading core/java/android/net/SocketKeepalive.java +4 −0 Original line number Original line Diff line number Diff line Loading @@ -43,6 +43,10 @@ import java.util.concurrent.Executor; * To stop an existing keepalive, call {@link SocketKeepalive#stop}. The system will call * To stop an existing keepalive, call {@link SocketKeepalive#stop}. The system will call * {@link SocketKeepalive.Callback#onStopped} if the operation was successful or * {@link SocketKeepalive.Callback#onStopped} if the operation was successful or * {@link SocketKeepalive.Callback#onError} if an error occurred. * {@link SocketKeepalive.Callback#onError} if an error occurred. * * The device SHOULD support keepalive offload. If it does not, it MUST reply with * {@link SocketKeepalive.Callback#onError} with {@code ERROR_UNSUPPORTED} to any keepalive offload * request. If it does, it MUST support at least 3 concurrent keepalive slots per transport. */ */ public abstract class SocketKeepalive implements AutoCloseable { public abstract class SocketKeepalive implements AutoCloseable { static final String TAG = "SocketKeepalive"; static final String TAG = "SocketKeepalive"; Loading core/java/android/net/util/KeepaliveUtils.java 0 → 100644 +121 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2019 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 android.net.util; import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.net.NetworkCapabilities; import android.text.TextUtils; import android.util.AndroidRuntimeException; import com.android.internal.R; /** * Collection of utilities for socket keepalive offload. * * @hide */ public final class KeepaliveUtils { public static final String TAG = "KeepaliveUtils"; // Minimum supported keepalive count per transport if the network supports keepalive. public static final int MIN_SUPPORTED_KEEPALIVE_COUNT = 3; public static class KeepaliveDeviceConfigurationException extends AndroidRuntimeException { public KeepaliveDeviceConfigurationException(final String msg) { super(msg); } } /** * Read supported keepalive count for each transport type from overlay resource. This should be * used to create a local variable store of resource customization, and use it as the input for * {@link getSupportedKeepalivesForNetworkCapabilities}. * * @param context The context to read resource from. * @return An array of supported keepalive count for each transport type. */ @NonNull public static int[] getSupportedKeepalives(@NonNull Context context) { String[] res = null; try { res = context.getResources().getStringArray( R.array.config_networkSupportedKeepaliveCount); } catch (Resources.NotFoundException unused) { } if (res == null) throw new KeepaliveDeviceConfigurationException("invalid resource"); final int[] ret = new int[NetworkCapabilities.MAX_TRANSPORT + 1]; for (final String row : res) { if (TextUtils.isEmpty(row)) { throw new KeepaliveDeviceConfigurationException("Empty string"); } final String[] arr = row.split(","); if (arr.length != 2) { throw new KeepaliveDeviceConfigurationException("Invalid parameter length"); } int transport; int supported; try { transport = Integer.parseInt(arr[0]); supported = Integer.parseInt(arr[1]); } catch (NumberFormatException e) { throw new KeepaliveDeviceConfigurationException("Invalid number format"); } if (!NetworkCapabilities.isValidTransport(transport)) { throw new KeepaliveDeviceConfigurationException("Invalid transport " + transport); } // Customized values should be either 0 to indicate the network doesn't support // keepalive offload, or a positive value that is at least // MIN_SUPPORTED_KEEPALIVE_COUNT if supported. if (supported != 0 && supported < MIN_SUPPORTED_KEEPALIVE_COUNT) { throw new KeepaliveDeviceConfigurationException( "Invalid supported count " + supported + " for " + NetworkCapabilities.transportNameOf(transport)); } ret[transport] = supported; } return ret; } /** * Get supported keepalive count for the given {@link NetworkCapabilities}. * * @param supportedKeepalives An array of supported keepalive count for each transport type. * @param nc The {@link NetworkCapabilities} of the network the socket keepalive is on. * * @return Supported keepalive count for the given {@link NetworkCapabilities}. */ public static int getSupportedKeepalivesForNetworkCapabilities( @NonNull int[] supportedKeepalives, @NonNull NetworkCapabilities nc) { final int[] transports = nc.getTransportTypes(); if (transports.length == 0) return 0; int supportedCount = supportedKeepalives[transports[0]]; // Iterate through transports and return minimum supported value. for (final int transport : transports) { if (supportedCount > supportedKeepalives[transport]) { supportedCount = supportedKeepalives[transport]; } } return supportedCount; } } core/res/res/values/config.xml +16 −0 Original line number Original line Diff line number Diff line Loading @@ -358,6 +358,22 @@ default value of that setting. --> default value of that setting. --> <integer translatable="false" name="config_networkDefaultDailyMultipathQuotaBytes">2500000</integer> <integer translatable="false" name="config_networkDefaultDailyMultipathQuotaBytes">2500000</integer> <!-- Default supported concurrent socket keepalive slots per transport type, used by ConnectivityManager.createSocketKeepalive() for calculating the number of keepalive offload slots that should be reserved for privileged access. This string array should be overridden by the device to present the capability of creating socket keepalives. --> <!-- An Array of "[NetworkCapabilities.TRANSPORT_*],[supported keepalives] --> <string-array translatable="false" name="config_networkSupportedKeepaliveCount"> <item>0,3</item> <item>1,3</item> </string-array> <!-- Reserved privileged keepalive slots per transport. --> <integer translatable="false" name="config_reservedPrivilegedKeepaliveSlots">2</integer> <!-- Allowed unprivileged keepalive slots per uid. --> <integer translatable="false" name="config_allowedUnprivilegedKeepalivePerUid">2</integer> <!-- List of regexpressions describing the interface (if any) that represent tetherable <!-- 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 USB interfaces. If the device doesn't want to support tethering over USB this should be empty. An example would be "usb.*" --> be empty. An example would be "usb.*" --> Loading core/res/res/values/symbols.xml +3 −0 Original line number Original line Diff line number Diff line Loading @@ -2026,6 +2026,9 @@ <java-symbol type="array" name="config_apfEthTypeBlackList" /> <java-symbol type="array" name="config_apfEthTypeBlackList" /> <java-symbol type="integer" name="config_networkDefaultDailyMultipathQuotaBytes" /> <java-symbol type="integer" name="config_networkDefaultDailyMultipathQuotaBytes" /> <java-symbol type="integer" name="config_networkMeteredMultipathPreference" /> <java-symbol type="integer" name="config_networkMeteredMultipathPreference" /> <java-symbol type="array" name="config_networkSupportedKeepaliveCount" /> <java-symbol type="integer" name="config_reservedPrivilegedKeepaliveSlots" /> <java-symbol type="integer" name="config_allowedUnprivilegedKeepalivePerUid" /> <java-symbol type="integer" name="config_notificationsBatteryFullARGB" /> <java-symbol type="integer" name="config_notificationsBatteryFullARGB" /> <java-symbol type="integer" name="config_notificationsBatteryLedOff" /> <java-symbol type="integer" name="config_notificationsBatteryLedOff" /> <java-symbol type="integer" name="config_notificationsBatteryLedOn" /> <java-symbol type="integer" name="config_notificationsBatteryLedOn" /> Loading services/core/java/com/android/server/connectivity/KeepaliveTracker.java +63 −12 Original line number Original line Diff line number Diff line Loading @@ -29,6 +29,7 @@ import static android.net.SocketKeepalive.ERROR_INVALID_INTERVAL; import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK; import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK; import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET; import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET; import static android.net.SocketKeepalive.ERROR_UNSUPPORTED; import static android.net.SocketKeepalive.MAX_INTERVAL_SEC; import static android.net.SocketKeepalive.MAX_INTERVAL_SEC; import static android.net.SocketKeepalive.MIN_INTERVAL_SEC; import static android.net.SocketKeepalive.MIN_INTERVAL_SEC; import static android.net.SocketKeepalive.NO_KEEPALIVE; import static android.net.SocketKeepalive.NO_KEEPALIVE; Loading @@ -46,6 +47,7 @@ import android.net.SocketKeepalive.InvalidPacketException; import android.net.SocketKeepalive.InvalidSocketException; import android.net.SocketKeepalive.InvalidSocketException; import android.net.TcpKeepalivePacketData; import android.net.TcpKeepalivePacketData; import android.net.util.IpUtils; import android.net.util.IpUtils; import android.net.util.KeepaliveUtils; import android.os.Binder; import android.os.Binder; import android.os.Handler; import android.os.Handler; import android.os.IBinder; import android.os.IBinder; Loading @@ -57,6 +59,7 @@ import android.system.Os; import android.util.Log; import android.util.Log; import android.util.Pair; import android.util.Pair; import com.android.internal.R; import com.android.internal.util.HexDump; import com.android.internal.util.HexDump; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter; Loading @@ -65,6 +68,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.SocketAddress; import java.util.ArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashMap; /** /** Loading @@ -90,10 +94,29 @@ public class KeepaliveTracker { @NonNull @NonNull private final Context mContext; private final Context mContext; // Supported keepalive count for each transport type, can be configured through // config_networkSupportedKeepaliveCount. For better error handling, use // {@link getSupportedKeepalivesForNetworkCapabilities} instead of direct access. @NonNull private final int[] mSupportedKeepalives; // Reserved privileged keepalive slots per transport. Caller's permission will be enforced if // the number of remaining keepalive slots is less than or equal to the threshold. private final int mReservedPrivilegedSlots; // Allowed unprivileged keepalive slots per uid. Caller's permission will be enforced if // the number of remaining keepalive slots is less than or equal to the threshold. private final int mAllowedUnprivilegedSlotsForUid; public KeepaliveTracker(Context context, Handler handler) { public KeepaliveTracker(Context context, Handler handler) { mConnectivityServiceHandler = handler; mConnectivityServiceHandler = handler; mTcpController = new TcpKeepaliveController(handler); mTcpController = new TcpKeepaliveController(handler); mContext = context; mContext = context; mSupportedKeepalives = KeepaliveUtils.getSupportedKeepalives(mContext); mReservedPrivilegedSlots = mContext.getResources().getInteger( R.integer.config_reservedPrivilegedKeepaliveSlots); mAllowedUnprivilegedSlotsForUid = mContext.getResources().getInteger( R.integer.config_allowedUnprivilegedKeepalivePerUid); } } /** /** Loading @@ -115,11 +138,6 @@ public class KeepaliveTracker { public static final int TYPE_NATT = 1; public static final int TYPE_NATT = 1; public static final int TYPE_TCP = 2; public static final int TYPE_TCP = 2; // Max allowed unprivileged keepalive slots per network. Caller's permission will be // enforced if number of existing keepalives reach this limit. // TODO: consider making this limit configurable via resources. private static final int MAX_UNPRIVILEGED_SLOTS = 3; // Keepalive slot. A small integer that identifies this keepalive among the ones handled // Keepalive slot. A small integer that identifies this keepalive among the ones handled // by this network. // by this network. private int mSlot = NO_KEEPALIVE; private int mSlot = NO_KEEPALIVE; Loading Loading @@ -246,24 +264,54 @@ public class KeepaliveTracker { private int checkPermission() { private int checkPermission() { final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai); final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai); int unprivilegedCount = 0; if (networkKeepalives == null) { if (networkKeepalives == null) { return ERROR_INVALID_NETWORK; return ERROR_INVALID_NETWORK; } } for (KeepaliveInfo ki : networkKeepalives.values()) { if (!ki.mPrivileged) { if (mPrivileged) return SUCCESS; unprivilegedCount++; final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities( mSupportedKeepalives, mNai.networkCapabilities); int takenUnprivilegedSlots = 0; for (final KeepaliveInfo ki : networkKeepalives.values()) { if (!ki.mPrivileged) ++takenUnprivilegedSlots; } if (takenUnprivilegedSlots > supported - mReservedPrivilegedSlots) { return ERROR_INSUFFICIENT_RESOURCES; } } if (unprivilegedCount >= MAX_UNPRIVILEGED_SLOTS) { return mPrivileged ? SUCCESS : ERROR_INSUFFICIENT_RESOURCES; // Count unprivileged keepalives for the same uid across networks. int unprivilegedCountSameUid = 0; for (final HashMap<Integer, KeepaliveInfo> kaForNetwork : mKeepalives.values()) { for (final KeepaliveInfo ki : kaForNetwork.values()) { if (ki.mUid == mUid) { unprivilegedCountSameUid++; } } } if (unprivilegedCountSameUid > mAllowedUnprivilegedSlotsForUid) { return ERROR_INSUFFICIENT_RESOURCES; } return SUCCESS; } } private int checkLimit() { final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai); if (networkKeepalives == null) { return ERROR_INVALID_NETWORK; } } final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities( mSupportedKeepalives, mNai.networkCapabilities); if (supported == 0) return ERROR_UNSUPPORTED; if (networkKeepalives.size() > supported) return ERROR_INSUFFICIENT_RESOURCES; return SUCCESS; return SUCCESS; } } private int isValid() { private int isValid() { synchronized (mNai) { synchronized (mNai) { int error = checkInterval(); int error = checkInterval(); if (error == SUCCESS) error = checkLimit(); if (error == SUCCESS) error = checkPermission(); if (error == SUCCESS) error = checkPermission(); if (error == SUCCESS) error = checkNetworkConnected(); if (error == SUCCESS) error = checkNetworkConnected(); if (error == SUCCESS) error = checkSourceAddress(); if (error == SUCCESS) error = checkSourceAddress(); Loading Loading @@ -642,6 +690,9 @@ public class KeepaliveTracker { } } public void dump(IndentingPrintWriter pw) { public void dump(IndentingPrintWriter pw) { pw.println("Supported Socket keepalives: " + Arrays.toString(mSupportedKeepalives)); pw.println("Reserved Privileged keepalives: " + mReservedPrivilegedSlots); pw.println("Allowed Unprivileged keepalives per uid: " + mAllowedUnprivilegedSlotsForUid); pw.println("Socket keepalives:"); pw.println("Socket keepalives:"); pw.increaseIndent(); pw.increaseIndent(); for (NetworkAgentInfo nai : mKeepalives.keySet()) { for (NetworkAgentInfo nai : mKeepalives.keySet()) { Loading Loading
core/java/android/net/SocketKeepalive.java +4 −0 Original line number Original line Diff line number Diff line Loading @@ -43,6 +43,10 @@ import java.util.concurrent.Executor; * To stop an existing keepalive, call {@link SocketKeepalive#stop}. The system will call * To stop an existing keepalive, call {@link SocketKeepalive#stop}. The system will call * {@link SocketKeepalive.Callback#onStopped} if the operation was successful or * {@link SocketKeepalive.Callback#onStopped} if the operation was successful or * {@link SocketKeepalive.Callback#onError} if an error occurred. * {@link SocketKeepalive.Callback#onError} if an error occurred. * * The device SHOULD support keepalive offload. If it does not, it MUST reply with * {@link SocketKeepalive.Callback#onError} with {@code ERROR_UNSUPPORTED} to any keepalive offload * request. If it does, it MUST support at least 3 concurrent keepalive slots per transport. */ */ public abstract class SocketKeepalive implements AutoCloseable { public abstract class SocketKeepalive implements AutoCloseable { static final String TAG = "SocketKeepalive"; static final String TAG = "SocketKeepalive"; Loading
core/java/android/net/util/KeepaliveUtils.java 0 → 100644 +121 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2019 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 android.net.util; import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.net.NetworkCapabilities; import android.text.TextUtils; import android.util.AndroidRuntimeException; import com.android.internal.R; /** * Collection of utilities for socket keepalive offload. * * @hide */ public final class KeepaliveUtils { public static final String TAG = "KeepaliveUtils"; // Minimum supported keepalive count per transport if the network supports keepalive. public static final int MIN_SUPPORTED_KEEPALIVE_COUNT = 3; public static class KeepaliveDeviceConfigurationException extends AndroidRuntimeException { public KeepaliveDeviceConfigurationException(final String msg) { super(msg); } } /** * Read supported keepalive count for each transport type from overlay resource. This should be * used to create a local variable store of resource customization, and use it as the input for * {@link getSupportedKeepalivesForNetworkCapabilities}. * * @param context The context to read resource from. * @return An array of supported keepalive count for each transport type. */ @NonNull public static int[] getSupportedKeepalives(@NonNull Context context) { String[] res = null; try { res = context.getResources().getStringArray( R.array.config_networkSupportedKeepaliveCount); } catch (Resources.NotFoundException unused) { } if (res == null) throw new KeepaliveDeviceConfigurationException("invalid resource"); final int[] ret = new int[NetworkCapabilities.MAX_TRANSPORT + 1]; for (final String row : res) { if (TextUtils.isEmpty(row)) { throw new KeepaliveDeviceConfigurationException("Empty string"); } final String[] arr = row.split(","); if (arr.length != 2) { throw new KeepaliveDeviceConfigurationException("Invalid parameter length"); } int transport; int supported; try { transport = Integer.parseInt(arr[0]); supported = Integer.parseInt(arr[1]); } catch (NumberFormatException e) { throw new KeepaliveDeviceConfigurationException("Invalid number format"); } if (!NetworkCapabilities.isValidTransport(transport)) { throw new KeepaliveDeviceConfigurationException("Invalid transport " + transport); } // Customized values should be either 0 to indicate the network doesn't support // keepalive offload, or a positive value that is at least // MIN_SUPPORTED_KEEPALIVE_COUNT if supported. if (supported != 0 && supported < MIN_SUPPORTED_KEEPALIVE_COUNT) { throw new KeepaliveDeviceConfigurationException( "Invalid supported count " + supported + " for " + NetworkCapabilities.transportNameOf(transport)); } ret[transport] = supported; } return ret; } /** * Get supported keepalive count for the given {@link NetworkCapabilities}. * * @param supportedKeepalives An array of supported keepalive count for each transport type. * @param nc The {@link NetworkCapabilities} of the network the socket keepalive is on. * * @return Supported keepalive count for the given {@link NetworkCapabilities}. */ public static int getSupportedKeepalivesForNetworkCapabilities( @NonNull int[] supportedKeepalives, @NonNull NetworkCapabilities nc) { final int[] transports = nc.getTransportTypes(); if (transports.length == 0) return 0; int supportedCount = supportedKeepalives[transports[0]]; // Iterate through transports and return minimum supported value. for (final int transport : transports) { if (supportedCount > supportedKeepalives[transport]) { supportedCount = supportedKeepalives[transport]; } } return supportedCount; } }
core/res/res/values/config.xml +16 −0 Original line number Original line Diff line number Diff line Loading @@ -358,6 +358,22 @@ default value of that setting. --> default value of that setting. --> <integer translatable="false" name="config_networkDefaultDailyMultipathQuotaBytes">2500000</integer> <integer translatable="false" name="config_networkDefaultDailyMultipathQuotaBytes">2500000</integer> <!-- Default supported concurrent socket keepalive slots per transport type, used by ConnectivityManager.createSocketKeepalive() for calculating the number of keepalive offload slots that should be reserved for privileged access. This string array should be overridden by the device to present the capability of creating socket keepalives. --> <!-- An Array of "[NetworkCapabilities.TRANSPORT_*],[supported keepalives] --> <string-array translatable="false" name="config_networkSupportedKeepaliveCount"> <item>0,3</item> <item>1,3</item> </string-array> <!-- Reserved privileged keepalive slots per transport. --> <integer translatable="false" name="config_reservedPrivilegedKeepaliveSlots">2</integer> <!-- Allowed unprivileged keepalive slots per uid. --> <integer translatable="false" name="config_allowedUnprivilegedKeepalivePerUid">2</integer> <!-- List of regexpressions describing the interface (if any) that represent tetherable <!-- 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 USB interfaces. If the device doesn't want to support tethering over USB this should be empty. An example would be "usb.*" --> be empty. An example would be "usb.*" --> Loading
core/res/res/values/symbols.xml +3 −0 Original line number Original line Diff line number Diff line Loading @@ -2026,6 +2026,9 @@ <java-symbol type="array" name="config_apfEthTypeBlackList" /> <java-symbol type="array" name="config_apfEthTypeBlackList" /> <java-symbol type="integer" name="config_networkDefaultDailyMultipathQuotaBytes" /> <java-symbol type="integer" name="config_networkDefaultDailyMultipathQuotaBytes" /> <java-symbol type="integer" name="config_networkMeteredMultipathPreference" /> <java-symbol type="integer" name="config_networkMeteredMultipathPreference" /> <java-symbol type="array" name="config_networkSupportedKeepaliveCount" /> <java-symbol type="integer" name="config_reservedPrivilegedKeepaliveSlots" /> <java-symbol type="integer" name="config_allowedUnprivilegedKeepalivePerUid" /> <java-symbol type="integer" name="config_notificationsBatteryFullARGB" /> <java-symbol type="integer" name="config_notificationsBatteryFullARGB" /> <java-symbol type="integer" name="config_notificationsBatteryLedOff" /> <java-symbol type="integer" name="config_notificationsBatteryLedOff" /> <java-symbol type="integer" name="config_notificationsBatteryLedOn" /> <java-symbol type="integer" name="config_notificationsBatteryLedOn" /> Loading
services/core/java/com/android/server/connectivity/KeepaliveTracker.java +63 −12 Original line number Original line Diff line number Diff line Loading @@ -29,6 +29,7 @@ import static android.net.SocketKeepalive.ERROR_INVALID_INTERVAL; import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK; import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK; import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET; import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET; import static android.net.SocketKeepalive.ERROR_UNSUPPORTED; import static android.net.SocketKeepalive.MAX_INTERVAL_SEC; import static android.net.SocketKeepalive.MAX_INTERVAL_SEC; import static android.net.SocketKeepalive.MIN_INTERVAL_SEC; import static android.net.SocketKeepalive.MIN_INTERVAL_SEC; import static android.net.SocketKeepalive.NO_KEEPALIVE; import static android.net.SocketKeepalive.NO_KEEPALIVE; Loading @@ -46,6 +47,7 @@ import android.net.SocketKeepalive.InvalidPacketException; import android.net.SocketKeepalive.InvalidSocketException; import android.net.SocketKeepalive.InvalidSocketException; import android.net.TcpKeepalivePacketData; import android.net.TcpKeepalivePacketData; import android.net.util.IpUtils; import android.net.util.IpUtils; import android.net.util.KeepaliveUtils; import android.os.Binder; import android.os.Binder; import android.os.Handler; import android.os.Handler; import android.os.IBinder; import android.os.IBinder; Loading @@ -57,6 +59,7 @@ import android.system.Os; import android.util.Log; import android.util.Log; import android.util.Pair; import android.util.Pair; import com.android.internal.R; import com.android.internal.util.HexDump; import com.android.internal.util.HexDump; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter; Loading @@ -65,6 +68,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.SocketAddress; import java.util.ArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashMap; /** /** Loading @@ -90,10 +94,29 @@ public class KeepaliveTracker { @NonNull @NonNull private final Context mContext; private final Context mContext; // Supported keepalive count for each transport type, can be configured through // config_networkSupportedKeepaliveCount. For better error handling, use // {@link getSupportedKeepalivesForNetworkCapabilities} instead of direct access. @NonNull private final int[] mSupportedKeepalives; // Reserved privileged keepalive slots per transport. Caller's permission will be enforced if // the number of remaining keepalive slots is less than or equal to the threshold. private final int mReservedPrivilegedSlots; // Allowed unprivileged keepalive slots per uid. Caller's permission will be enforced if // the number of remaining keepalive slots is less than or equal to the threshold. private final int mAllowedUnprivilegedSlotsForUid; public KeepaliveTracker(Context context, Handler handler) { public KeepaliveTracker(Context context, Handler handler) { mConnectivityServiceHandler = handler; mConnectivityServiceHandler = handler; mTcpController = new TcpKeepaliveController(handler); mTcpController = new TcpKeepaliveController(handler); mContext = context; mContext = context; mSupportedKeepalives = KeepaliveUtils.getSupportedKeepalives(mContext); mReservedPrivilegedSlots = mContext.getResources().getInteger( R.integer.config_reservedPrivilegedKeepaliveSlots); mAllowedUnprivilegedSlotsForUid = mContext.getResources().getInteger( R.integer.config_allowedUnprivilegedKeepalivePerUid); } } /** /** Loading @@ -115,11 +138,6 @@ public class KeepaliveTracker { public static final int TYPE_NATT = 1; public static final int TYPE_NATT = 1; public static final int TYPE_TCP = 2; public static final int TYPE_TCP = 2; // Max allowed unprivileged keepalive slots per network. Caller's permission will be // enforced if number of existing keepalives reach this limit. // TODO: consider making this limit configurable via resources. private static final int MAX_UNPRIVILEGED_SLOTS = 3; // Keepalive slot. A small integer that identifies this keepalive among the ones handled // Keepalive slot. A small integer that identifies this keepalive among the ones handled // by this network. // by this network. private int mSlot = NO_KEEPALIVE; private int mSlot = NO_KEEPALIVE; Loading Loading @@ -246,24 +264,54 @@ public class KeepaliveTracker { private int checkPermission() { private int checkPermission() { final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai); final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai); int unprivilegedCount = 0; if (networkKeepalives == null) { if (networkKeepalives == null) { return ERROR_INVALID_NETWORK; return ERROR_INVALID_NETWORK; } } for (KeepaliveInfo ki : networkKeepalives.values()) { if (!ki.mPrivileged) { if (mPrivileged) return SUCCESS; unprivilegedCount++; final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities( mSupportedKeepalives, mNai.networkCapabilities); int takenUnprivilegedSlots = 0; for (final KeepaliveInfo ki : networkKeepalives.values()) { if (!ki.mPrivileged) ++takenUnprivilegedSlots; } if (takenUnprivilegedSlots > supported - mReservedPrivilegedSlots) { return ERROR_INSUFFICIENT_RESOURCES; } } if (unprivilegedCount >= MAX_UNPRIVILEGED_SLOTS) { return mPrivileged ? SUCCESS : ERROR_INSUFFICIENT_RESOURCES; // Count unprivileged keepalives for the same uid across networks. int unprivilegedCountSameUid = 0; for (final HashMap<Integer, KeepaliveInfo> kaForNetwork : mKeepalives.values()) { for (final KeepaliveInfo ki : kaForNetwork.values()) { if (ki.mUid == mUid) { unprivilegedCountSameUid++; } } } if (unprivilegedCountSameUid > mAllowedUnprivilegedSlotsForUid) { return ERROR_INSUFFICIENT_RESOURCES; } return SUCCESS; } } private int checkLimit() { final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai); if (networkKeepalives == null) { return ERROR_INVALID_NETWORK; } } final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities( mSupportedKeepalives, mNai.networkCapabilities); if (supported == 0) return ERROR_UNSUPPORTED; if (networkKeepalives.size() > supported) return ERROR_INSUFFICIENT_RESOURCES; return SUCCESS; return SUCCESS; } } private int isValid() { private int isValid() { synchronized (mNai) { synchronized (mNai) { int error = checkInterval(); int error = checkInterval(); if (error == SUCCESS) error = checkLimit(); if (error == SUCCESS) error = checkPermission(); if (error == SUCCESS) error = checkPermission(); if (error == SUCCESS) error = checkNetworkConnected(); if (error == SUCCESS) error = checkNetworkConnected(); if (error == SUCCESS) error = checkSourceAddress(); if (error == SUCCESS) error = checkSourceAddress(); Loading Loading @@ -642,6 +690,9 @@ public class KeepaliveTracker { } } public void dump(IndentingPrintWriter pw) { public void dump(IndentingPrintWriter pw) { pw.println("Supported Socket keepalives: " + Arrays.toString(mSupportedKeepalives)); pw.println("Reserved Privileged keepalives: " + mReservedPrivilegedSlots); pw.println("Allowed Unprivileged keepalives per uid: " + mAllowedUnprivilegedSlotsForUid); pw.println("Socket keepalives:"); pw.println("Socket keepalives:"); pw.increaseIndent(); pw.increaseIndent(); for (NetworkAgentInfo nai : mKeepalives.keySet()) { for (NetworkAgentInfo nai : mKeepalives.keySet()) { Loading