Cache network capabilities.
Cache network capabilities to reduce the number of calls
ConnectivityController makes to ConnectivityManager.
Bug: 164460449
Bug: 166702583
Test: atest com.android.server.job.controllers.ConnectivityControllerTest
Test: atest CtsJobSchedulerTestCases
Change-Id: I501e5153d7bffd2494aec484c74b9e1b6252aeeb
(cherry picked from commit 6f9ab029005993e3674f5ccaa321f9bc4467d5b8)
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index bb94275..ba83483 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -22,6 +22,7 @@
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
+import android.annotation.Nullable;
import android.app.job.JobInfo;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
@@ -86,9 +87,12 @@
@GuardedBy("mLock")
private final SparseArray<ArraySet<JobStatus>> mRequestedWhitelistJobs = new SparseArray<>();
- /** List of currently available networks. */
+ /**
+ * Set of currently available networks mapped to their latest network capabilities. Cache the
+ * latest capabilities to avoid unnecessary calls into ConnectivityManager.
+ */
@GuardedBy("mLock")
- private final ArraySet<Network> mAvailableNetworks = new ArraySet<>();
+ private final ArrayMap<Network, NetworkCapabilities> mAvailableNetworks = new ArrayMap<>();
private static final int MSG_DATA_SAVER_TOGGLED = 0;
private static final int MSG_UID_RULES_CHANGES = 1;
@@ -165,9 +169,8 @@
public boolean isNetworkAvailable(JobStatus job) {
synchronized (mLock) {
for (int i = 0; i < mAvailableNetworks.size(); ++i) {
- final Network network = mAvailableNetworks.valueAt(i);
- final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(
- network);
+ final Network network = mAvailableNetworks.keyAt(i);
+ final NetworkCapabilities capabilities = mAvailableNetworks.valueAt(i);
final boolean satisfied = isSatisfied(job, network, capabilities, mConstants);
if (DEBUG) {
Slog.v(TAG, "isNetworkAvailable(" + job + ") with network " + network
@@ -427,9 +430,33 @@
return false;
}
+ @Nullable
+ private NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
+ if (network == null) {
+ return null;
+ }
+ synchronized (mLock) {
+ // There is technically a race here if the Network object is reused. This can happen
+ // only if that Network disconnects and the auto-incrementing network ID in
+ // ConnectivityService wraps. This should no longer be a concern if/when we only make
+ // use of asynchronous calls.
+ if (mAvailableNetworks.get(network) != null) {
+ return mAvailableNetworks.get(network);
+ }
+
+ // This should almost never happen because any time a new network connects, the
+ // NetworkCallback would populate mAvailableNetworks. However, it's currently necessary
+ // because we also call synchronous methods such as getActiveNetworkForUid.
+ // TODO(134978280): remove after switching to callback-based APIs
+ final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network);
+ mAvailableNetworks.put(network, capabilities);
+ return capabilities;
+ }
+ }
+
private boolean updateConstraintsSatisfied(JobStatus jobStatus) {
final Network network = mConnManager.getActiveNetworkForUid(jobStatus.getSourceUid());
- final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network);
+ final NetworkCapabilities capabilities = getNetworkCapabilities(network);
return updateConstraintsSatisfied(jobStatus, network, capabilities);
}
@@ -470,19 +497,13 @@
*/
private void updateTrackedJobs(int filterUid, Network filterNetwork) {
synchronized (mLock) {
- // Since this is a really hot codepath, temporarily cache any
- // answers that we get from ConnectivityManager.
- final ArrayMap<Network, NetworkCapabilities> networkToCapabilities = new ArrayMap<>();
-
boolean changed = false;
if (filterUid == -1) {
for (int i = mTrackedJobs.size() - 1; i >= 0; i--) {
- changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i),
- filterNetwork, networkToCapabilities);
+ changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i), filterNetwork);
}
} else {
- changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid),
- filterNetwork, networkToCapabilities);
+ changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid), filterNetwork);
}
if (changed) {
mStateChangedListener.onControllerStateChanged();
@@ -490,18 +511,13 @@
}
}
- private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork,
- ArrayMap<Network, NetworkCapabilities> networkToCapabilities) {
+ private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork) {
if (jobs == null || jobs.size() == 0) {
return false;
}
final Network network = mConnManager.getActiveNetworkForUid(jobs.valueAt(0).getSourceUid());
- NetworkCapabilities capabilities = networkToCapabilities.get(network);
- if (capabilities == null) {
- capabilities = mConnManager.getNetworkCapabilities(network);
- networkToCapabilities.put(network, capabilities);
- }
+ final NetworkCapabilities capabilities = getNetworkCapabilities(network);
final boolean networkMatch = (filterNetwork == null
|| Objects.equals(filterNetwork, network));
@@ -544,9 +560,9 @@
@Override
public void onAvailable(Network network) {
if (DEBUG) Slog.v(TAG, "onAvailable: " + network);
- synchronized (mLock) {
- mAvailableNetworks.add(network);
- }
+ // Documentation says not to call getNetworkCapabilities here but wait for
+ // onCapabilitiesChanged instead. onCapabilitiesChanged should be called immediately
+ // after this, so no need to update mAvailableNetworks here.
}
@Override
@@ -554,6 +570,9 @@
if (DEBUG) {
Slog.v(TAG, "onCapabilitiesChanged: " + network);
}
+ synchronized (mLock) {
+ mAvailableNetworks.put(network, capabilities);
+ }
updateTrackedJobs(-1, network);
}
@@ -630,6 +649,8 @@
pw.println("Available networks:");
pw.increaseIndent();
for (int i = 0; i < mAvailableNetworks.size(); i++) {
+ pw.print(mAvailableNetworks.keyAt(i));
+ pw.print(": ");
pw.println(mAvailableNetworks.valueAt(i));
}
pw.decreaseIndent();
@@ -667,7 +688,7 @@
mRequestedWhitelistJobs.keyAt(i));
}
for (int i = 0; i < mAvailableNetworks.size(); i++) {
- Network network = mAvailableNetworks.valueAt(i);
+ Network network = mAvailableNetworks.keyAt(i);
if (network != null) {
network.dumpDebug(proto,
StateControllerProto.ConnectivityController.AVAILABLE_NETWORKS);