[BR06] Support check whether network is blocked by data saver
This change adds a DataSaverStatusTracker, which is a helper class to continuously track data saver status through NPMS public API and intents. ConnectivityManager#isUidNetworkingBlocked would use this cached information along with bpf maps to decide whether networking of an uid is blocked. Test: atest FrameworksNetTests:android.net.connectivity.android.net.BpfNetMapsReaderTest Test: atest ConnectivityCoverageTests:android.net.connectivity.android.net.ConnectivityManagerTest Bug: 297836825 Change-Id: I7e13191759430f3ea1f4dec7facc02f16be7146d
This commit is contained in:
parent
e003152e2c
commit
c3dc5b6222
1 changed files with 246 additions and 18 deletions
|
|
@ -16,6 +16,8 @@
|
|||
package android.net;
|
||||
|
||||
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
|
||||
import static android.content.pm.ApplicationInfo.FLAG_PERSISTENT;
|
||||
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
|
||||
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
|
||||
import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
|
||||
import static android.net.NetworkRequest.Type.LISTEN;
|
||||
|
|
@ -25,6 +27,8 @@ import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
|
|||
import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT;
|
||||
import static android.net.QosCallback.QosCallbackRegistrationException;
|
||||
|
||||
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.FlaggedApi;
|
||||
import android.annotation.IntDef;
|
||||
|
|
@ -37,12 +41,16 @@ import android.annotation.SdkConstant.SdkConstantType;
|
|||
import android.annotation.SuppressLint;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.SystemService;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Application;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityDiagnosticsManager.DataStallReport.DetectionMethod;
|
||||
import android.net.IpSecManager.UdpEncapsulationSocket;
|
||||
import android.net.SocketKeepalive.Callback;
|
||||
|
|
@ -74,6 +82,7 @@ import android.util.Range;
|
|||
import android.util.SparseIntArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import libcore.net.event.NetworkEventDispatcher;
|
||||
|
||||
|
|
@ -95,6 +104,7 @@ import java.util.concurrent.Executor;
|
|||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Class that answers queries about the state of network connectivity. It also
|
||||
|
|
@ -6199,13 +6209,99 @@ public class ConnectivityManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return whether the network is blocked for the given uid.
|
||||
* Helper class to track data saver status.
|
||||
*
|
||||
* The class will fetch current data saver status from {@link NetworkPolicyManager} when
|
||||
* initialized, and listening for status changed intent to cache the latest status.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.TIRAMISU) // RECEIVER_NOT_EXPORTED requires T.
|
||||
@VisibleForTesting(visibility = PRIVATE)
|
||||
public static class DataSaverStatusTracker extends BroadcastReceiver {
|
||||
private static final Object sDataSaverStatusTrackerLock = new Object();
|
||||
|
||||
private static volatile DataSaverStatusTracker sInstance;
|
||||
|
||||
/**
|
||||
* Gets a static instance of the class.
|
||||
*
|
||||
* @param context A {@link Context} for initialization. Note that since the data saver
|
||||
* status is global on a device, passing any context is equivalent.
|
||||
* @return The static instance of a {@link DataSaverStatusTracker}.
|
||||
*/
|
||||
public static DataSaverStatusTracker getInstance(@NonNull Context context) {
|
||||
if (sInstance == null) {
|
||||
synchronized (sDataSaverStatusTrackerLock) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new DataSaverStatusTracker(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private final NetworkPolicyManager mNpm;
|
||||
// The value updates on the caller's binder thread or UI thread.
|
||||
private final AtomicBoolean mIsDataSaverEnabled;
|
||||
|
||||
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||
public DataSaverStatusTracker(final Context context) {
|
||||
// To avoid leaks, take the application context.
|
||||
final Context appContext;
|
||||
if (context instanceof Application) {
|
||||
appContext = context;
|
||||
} else {
|
||||
appContext = context.getApplicationContext();
|
||||
}
|
||||
|
||||
if ((appContext.getApplicationInfo().flags & FLAG_PERSISTENT) == 0
|
||||
&& (appContext.getApplicationInfo().flags & FLAG_SYSTEM) == 0) {
|
||||
throw new IllegalStateException("Unexpected caller: "
|
||||
+ appContext.getApplicationInfo().packageName);
|
||||
}
|
||||
|
||||
mNpm = appContext.getSystemService(NetworkPolicyManager.class);
|
||||
final IntentFilter filter = new IntentFilter(
|
||||
ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED);
|
||||
// The receiver should not receive broadcasts from other Apps.
|
||||
appContext.registerReceiver(this, filter, Context.RECEIVER_NOT_EXPORTED);
|
||||
mIsDataSaverEnabled = new AtomicBoolean();
|
||||
updateDataSaverEnabled();
|
||||
}
|
||||
|
||||
// Runs on caller's UI thread.
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED)) {
|
||||
updateDataSaverEnabled();
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected intent " + intent);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getDataSaverEnabled() {
|
||||
return mIsDataSaverEnabled.get();
|
||||
}
|
||||
|
||||
private void updateDataSaverEnabled() {
|
||||
// Uid doesn't really matter, but use a fixed UID to make things clearer.
|
||||
final int dataSaverForCallerUid = mNpm.getRestrictBackgroundStatus(Process.SYSTEM_UID);
|
||||
mIsDataSaverEnabled.set(dataSaverForCallerUid
|
||||
!= ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the network is blocked for the given uid and metered condition.
|
||||
*
|
||||
* Similar to {@link NetworkPolicyManager#isUidNetworkingBlocked}, but directly reads the BPF
|
||||
* maps and therefore considerably faster. For use by the NetworkStack process only.
|
||||
*
|
||||
* @param uid The target uid.
|
||||
* @return True if all networking is blocked. Otherwise, false.
|
||||
* @param isNetworkMetered Whether the target network is metered.
|
||||
*
|
||||
* @return True if all networking with the given condition is blocked. Otherwise, false.
|
||||
* @throws IllegalStateException if the map cannot be opened.
|
||||
* @throws ServiceSpecificException if the read fails.
|
||||
* @hide
|
||||
|
|
@ -6219,13 +6315,16 @@ public class ConnectivityManager {
|
|||
// @SystemApi(client = MODULE_LIBRARIES)
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU) // BPF maps were only mainlined in T
|
||||
@RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
|
||||
public boolean isUidNetworkingBlocked(int uid) {
|
||||
public boolean isUidNetworkingBlocked(int uid, boolean isNetworkMetered) {
|
||||
final BpfNetMapsReader reader = BpfNetMapsReader.getInstance();
|
||||
|
||||
return reader.isUidBlockedByFirewallChains(uid);
|
||||
final boolean isDataSaverEnabled;
|
||||
// TODO: For U-QPR3+ devices, get data saver status from bpf configuration map directly.
|
||||
final DataSaverStatusTracker dataSaverStatusTracker =
|
||||
DataSaverStatusTracker.getInstance(mContext);
|
||||
isDataSaverEnabled = dataSaverStatusTracker.getDataSaverEnabled();
|
||||
|
||||
// TODO: If isNetworkMetered is true, check the data saver switch, penalty box
|
||||
// and happy box rules.
|
||||
return reader.isUidNetworkingBlocked(uid, isNetworkMetered, isDataSaverEnabled);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
|
|
|
|||
Loading…
Reference in a new issue