Loading services/core/java/com/android/server/job/controllers/ConnectivityController.java +87 −54 Original line number Original line Diff line number Diff line Loading @@ -55,6 +55,8 @@ import java.util.function.Predicate; * Each app can have a different default networks or different connectivity * Each app can have a different default networks or different connectivity * status due to user-requested network policies, so we need to check * status due to user-requested network policies, so we need to check * constraints on a per-UID basis. * constraints on a per-UID basis. * * Test: atest com.android.server.job.controllers.ConnectivityControllerTest */ */ public final class ConnectivityController extends StateController implements public final class ConnectivityController extends StateController implements ConnectivityManager.OnNetworkActiveListener { ConnectivityManager.OnNetworkActiveListener { Loading @@ -65,8 +67,9 @@ public final class ConnectivityController extends StateController implements private final ConnectivityManager mConnManager; private final ConnectivityManager mConnManager; private final NetworkPolicyManager mNetPolicyManager; private final NetworkPolicyManager mNetPolicyManager; /** List of tracked jobs keyed by source UID. */ @GuardedBy("mLock") @GuardedBy("mLock") private final ArraySet<JobStatus> mTrackedJobs = new ArraySet<>(); private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>(); public ConnectivityController(JobSchedulerService service) { public ConnectivityController(JobSchedulerService service) { super(service); super(service); Loading @@ -87,7 +90,12 @@ public final class ConnectivityController extends StateController implements public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { if (jobStatus.hasConnectivityConstraint()) { if (jobStatus.hasConnectivityConstraint()) { updateConstraintsSatisfied(jobStatus); updateConstraintsSatisfied(jobStatus); mTrackedJobs.add(jobStatus); ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUid()); if (jobs == null) { jobs = new ArraySet<>(); mTrackedJobs.put(jobStatus.getSourceUid(), jobs); } jobs.add(jobStatus); jobStatus.setTrackingController(JobStatus.TRACKING_CONNECTIVITY); jobStatus.setTrackingController(JobStatus.TRACKING_CONNECTIVITY); } } } } Loading @@ -97,7 +105,10 @@ public final class ConnectivityController extends StateController implements public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) { boolean forUpdate) { if (jobStatus.clearTrackingController(JobStatus.TRACKING_CONNECTIVITY)) { if (jobStatus.clearTrackingController(JobStatus.TRACKING_CONNECTIVITY)) { mTrackedJobs.remove(jobStatus); ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUid()); if (jobs != null) { jobs.remove(jobStatus); } } } } } Loading Loading @@ -244,43 +255,52 @@ public final class ConnectivityController extends StateController implements synchronized (mLock) { synchronized (mLock) { // Since this is a really hot codepath, temporarily cache any // Since this is a really hot codepath, temporarily cache any // answers that we get from ConnectivityManager. // answers that we get from ConnectivityManager. final SparseArray<Network> uidToNetwork = new SparseArray<>(); final SparseArray<NetworkCapabilities> networkToCapabilities = new SparseArray<>(); final SparseArray<NetworkCapabilities> networkToCapabilities = new SparseArray<>(); boolean changed = false; boolean changed = false; if (filterUid == -1) { for (int i = mTrackedJobs.size() - 1; i >= 0; i--) { for (int i = mTrackedJobs.size() - 1; i >= 0; i--) { final JobStatus js = mTrackedJobs.valueAt(i); changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i), final int uid = js.getSourceUid(); filterNetwork, networkToCapabilities); } } else { changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid), filterNetwork, networkToCapabilities); } if (changed) { mStateChangedListener.onControllerStateChanged(); } } } final boolean uidMatch = (filterUid == -1 || filterUid == uid); private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork, if (uidMatch) { SparseArray<NetworkCapabilities> networkToCapabilities) { Network network = uidToNetwork.get(uid); if (jobs == null || jobs.size() == 0) { if (network == null) { return false; network = mConnManager.getActiveNetworkForUid(uid); uidToNetwork.put(uid, network); } } // Update either when we have a network match, or when the final Network network = mConnManager.getActiveNetworkForUid(jobs.valueAt(0).getSourceUid()); // job hasn't yet been evaluated against the currently // active network; typically when we just lost a network. final boolean networkMatch = (filterNetwork == null || Objects.equals(filterNetwork, network)); final boolean forceUpdate = !Objects.equals(js.network, network); if (networkMatch || forceUpdate) { final int netId = network != null ? network.netId : -1; final int netId = network != null ? network.netId : -1; NetworkCapabilities capabilities = networkToCapabilities.get(netId); NetworkCapabilities capabilities = networkToCapabilities.get(netId); if (capabilities == null) { if (capabilities == null) { capabilities = mConnManager.getNetworkCapabilities(network); capabilities = mConnManager.getNetworkCapabilities(network); networkToCapabilities.put(netId, capabilities); networkToCapabilities.put(netId, capabilities); } } final boolean networkMatch = (filterNetwork == null || Objects.equals(filterNetwork, network)); boolean changed = false; for (int i = jobs.size() - 1; i >= 0; i--) { final JobStatus js = jobs.valueAt(i); // Update either when we have a network match, or when the // job hasn't yet been evaluated against the currently // active network; typically when we just lost a network. if (networkMatch || !Objects.equals(js.network, network)) { changed |= updateConstraintsSatisfied(js, network, capabilities); changed |= updateConstraintsSatisfied(js, network, capabilities); } } } } } return changed; if (changed) { mStateChangedListener.onControllerStateChanged(); } } } } /** /** Loading @@ -290,7 +310,9 @@ public final class ConnectivityController extends StateController implements public void onNetworkActive() { public void onNetworkActive() { synchronized (mLock) { synchronized (mLock) { for (int i = mTrackedJobs.size()-1; i >= 0; i--) { for (int i = mTrackedJobs.size()-1; i >= 0; i--) { final JobStatus js = mTrackedJobs.valueAt(i); final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i); for (int j = jobs.size() - 1; j >= 0; j--) { final JobStatus js = jobs.valueAt(j); if (js.isReady()) { if (js.isReady()) { if (DEBUG) { if (DEBUG) { Slog.d(TAG, "Running " + js + " due to network activity."); Slog.d(TAG, "Running " + js + " due to network activity."); Loading @@ -300,6 +322,7 @@ public final class ConnectivityController extends StateController implements } } } } } } } private final NetworkCallback mNetworkCallback = new NetworkCallback() { private final NetworkCallback mNetworkCallback = new NetworkCallback() { @Override @Override Loading Loading @@ -334,8 +357,12 @@ public final class ConnectivityController extends StateController implements public void dumpControllerStateLocked(IndentingPrintWriter pw, public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) { Predicate<JobStatus> predicate) { for (int i = 0; i < mTrackedJobs.size(); i++) { for (int i = 0; i < mTrackedJobs.size(); i++) { final JobStatus js = mTrackedJobs.valueAt(i); final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i); if (predicate.test(js)) { for (int j = 0; j < jobs.size(); j++) { final JobStatus js = jobs.valueAt(j); if (!predicate.test(js)) { continue; } pw.print("#"); pw.print("#"); js.printUniqueId(pw); js.printUniqueId(pw); pw.print(" from "); pw.print(" from "); Loading @@ -355,21 +382,27 @@ public final class ConnectivityController extends StateController implements final long mToken = proto.start(StateControllerProto.CONNECTIVITY); final long mToken = proto.start(StateControllerProto.CONNECTIVITY); for (int i = 0; i < mTrackedJobs.size(); i++) { for (int i = 0; i < mTrackedJobs.size(); i++) { final JobStatus js = mTrackedJobs.valueAt(i); final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i); for (int j = 0; j < jobs.size(); j++) { final JobStatus js = jobs.valueAt(j); if (!predicate.test(js)) { if (!predicate.test(js)) { continue; continue; } } final long jsToken = proto.start(StateControllerProto.ConnectivityController.TRACKED_JOBS); final long jsToken = proto.start( js.writeToShortProto(proto, StateControllerProto.ConnectivityController.TrackedJob.INFO); StateControllerProto.ConnectivityController.TRACKED_JOBS); js.writeToShortProto(proto, StateControllerProto.ConnectivityController.TrackedJob.INFO); proto.write(StateControllerProto.ConnectivityController.TrackedJob.SOURCE_UID, proto.write(StateControllerProto.ConnectivityController.TrackedJob.SOURCE_UID, js.getSourceUid()); js.getSourceUid()); NetworkRequest rn = js.getJob().getRequiredNetwork(); NetworkRequest rn = js.getJob().getRequiredNetwork(); if (rn != null) { if (rn != null) { rn.writeToProto(proto, rn.writeToProto(proto, StateControllerProto.ConnectivityController.TrackedJob.REQUIRED_NETWORK); StateControllerProto.ConnectivityController.TrackedJob .REQUIRED_NETWORK); } } proto.end(jsToken); proto.end(jsToken); } } } proto.end(mToken); proto.end(mToken); proto.end(token); proto.end(token); Loading Loading
services/core/java/com/android/server/job/controllers/ConnectivityController.java +87 −54 Original line number Original line Diff line number Diff line Loading @@ -55,6 +55,8 @@ import java.util.function.Predicate; * Each app can have a different default networks or different connectivity * Each app can have a different default networks or different connectivity * status due to user-requested network policies, so we need to check * status due to user-requested network policies, so we need to check * constraints on a per-UID basis. * constraints on a per-UID basis. * * Test: atest com.android.server.job.controllers.ConnectivityControllerTest */ */ public final class ConnectivityController extends StateController implements public final class ConnectivityController extends StateController implements ConnectivityManager.OnNetworkActiveListener { ConnectivityManager.OnNetworkActiveListener { Loading @@ -65,8 +67,9 @@ public final class ConnectivityController extends StateController implements private final ConnectivityManager mConnManager; private final ConnectivityManager mConnManager; private final NetworkPolicyManager mNetPolicyManager; private final NetworkPolicyManager mNetPolicyManager; /** List of tracked jobs keyed by source UID. */ @GuardedBy("mLock") @GuardedBy("mLock") private final ArraySet<JobStatus> mTrackedJobs = new ArraySet<>(); private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>(); public ConnectivityController(JobSchedulerService service) { public ConnectivityController(JobSchedulerService service) { super(service); super(service); Loading @@ -87,7 +90,12 @@ public final class ConnectivityController extends StateController implements public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { if (jobStatus.hasConnectivityConstraint()) { if (jobStatus.hasConnectivityConstraint()) { updateConstraintsSatisfied(jobStatus); updateConstraintsSatisfied(jobStatus); mTrackedJobs.add(jobStatus); ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUid()); if (jobs == null) { jobs = new ArraySet<>(); mTrackedJobs.put(jobStatus.getSourceUid(), jobs); } jobs.add(jobStatus); jobStatus.setTrackingController(JobStatus.TRACKING_CONNECTIVITY); jobStatus.setTrackingController(JobStatus.TRACKING_CONNECTIVITY); } } } } Loading @@ -97,7 +105,10 @@ public final class ConnectivityController extends StateController implements public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) { boolean forUpdate) { if (jobStatus.clearTrackingController(JobStatus.TRACKING_CONNECTIVITY)) { if (jobStatus.clearTrackingController(JobStatus.TRACKING_CONNECTIVITY)) { mTrackedJobs.remove(jobStatus); ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUid()); if (jobs != null) { jobs.remove(jobStatus); } } } } } Loading Loading @@ -244,43 +255,52 @@ public final class ConnectivityController extends StateController implements synchronized (mLock) { synchronized (mLock) { // Since this is a really hot codepath, temporarily cache any // Since this is a really hot codepath, temporarily cache any // answers that we get from ConnectivityManager. // answers that we get from ConnectivityManager. final SparseArray<Network> uidToNetwork = new SparseArray<>(); final SparseArray<NetworkCapabilities> networkToCapabilities = new SparseArray<>(); final SparseArray<NetworkCapabilities> networkToCapabilities = new SparseArray<>(); boolean changed = false; boolean changed = false; if (filterUid == -1) { for (int i = mTrackedJobs.size() - 1; i >= 0; i--) { for (int i = mTrackedJobs.size() - 1; i >= 0; i--) { final JobStatus js = mTrackedJobs.valueAt(i); changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i), final int uid = js.getSourceUid(); filterNetwork, networkToCapabilities); } } else { changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid), filterNetwork, networkToCapabilities); } if (changed) { mStateChangedListener.onControllerStateChanged(); } } } final boolean uidMatch = (filterUid == -1 || filterUid == uid); private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork, if (uidMatch) { SparseArray<NetworkCapabilities> networkToCapabilities) { Network network = uidToNetwork.get(uid); if (jobs == null || jobs.size() == 0) { if (network == null) { return false; network = mConnManager.getActiveNetworkForUid(uid); uidToNetwork.put(uid, network); } } // Update either when we have a network match, or when the final Network network = mConnManager.getActiveNetworkForUid(jobs.valueAt(0).getSourceUid()); // job hasn't yet been evaluated against the currently // active network; typically when we just lost a network. final boolean networkMatch = (filterNetwork == null || Objects.equals(filterNetwork, network)); final boolean forceUpdate = !Objects.equals(js.network, network); if (networkMatch || forceUpdate) { final int netId = network != null ? network.netId : -1; final int netId = network != null ? network.netId : -1; NetworkCapabilities capabilities = networkToCapabilities.get(netId); NetworkCapabilities capabilities = networkToCapabilities.get(netId); if (capabilities == null) { if (capabilities == null) { capabilities = mConnManager.getNetworkCapabilities(network); capabilities = mConnManager.getNetworkCapabilities(network); networkToCapabilities.put(netId, capabilities); networkToCapabilities.put(netId, capabilities); } } final boolean networkMatch = (filterNetwork == null || Objects.equals(filterNetwork, network)); boolean changed = false; for (int i = jobs.size() - 1; i >= 0; i--) { final JobStatus js = jobs.valueAt(i); // Update either when we have a network match, or when the // job hasn't yet been evaluated against the currently // active network; typically when we just lost a network. if (networkMatch || !Objects.equals(js.network, network)) { changed |= updateConstraintsSatisfied(js, network, capabilities); changed |= updateConstraintsSatisfied(js, network, capabilities); } } } } } return changed; if (changed) { mStateChangedListener.onControllerStateChanged(); } } } } /** /** Loading @@ -290,7 +310,9 @@ public final class ConnectivityController extends StateController implements public void onNetworkActive() { public void onNetworkActive() { synchronized (mLock) { synchronized (mLock) { for (int i = mTrackedJobs.size()-1; i >= 0; i--) { for (int i = mTrackedJobs.size()-1; i >= 0; i--) { final JobStatus js = mTrackedJobs.valueAt(i); final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i); for (int j = jobs.size() - 1; j >= 0; j--) { final JobStatus js = jobs.valueAt(j); if (js.isReady()) { if (js.isReady()) { if (DEBUG) { if (DEBUG) { Slog.d(TAG, "Running " + js + " due to network activity."); Slog.d(TAG, "Running " + js + " due to network activity."); Loading @@ -300,6 +322,7 @@ public final class ConnectivityController extends StateController implements } } } } } } } private final NetworkCallback mNetworkCallback = new NetworkCallback() { private final NetworkCallback mNetworkCallback = new NetworkCallback() { @Override @Override Loading Loading @@ -334,8 +357,12 @@ public final class ConnectivityController extends StateController implements public void dumpControllerStateLocked(IndentingPrintWriter pw, public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) { Predicate<JobStatus> predicate) { for (int i = 0; i < mTrackedJobs.size(); i++) { for (int i = 0; i < mTrackedJobs.size(); i++) { final JobStatus js = mTrackedJobs.valueAt(i); final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i); if (predicate.test(js)) { for (int j = 0; j < jobs.size(); j++) { final JobStatus js = jobs.valueAt(j); if (!predicate.test(js)) { continue; } pw.print("#"); pw.print("#"); js.printUniqueId(pw); js.printUniqueId(pw); pw.print(" from "); pw.print(" from "); Loading @@ -355,21 +382,27 @@ public final class ConnectivityController extends StateController implements final long mToken = proto.start(StateControllerProto.CONNECTIVITY); final long mToken = proto.start(StateControllerProto.CONNECTIVITY); for (int i = 0; i < mTrackedJobs.size(); i++) { for (int i = 0; i < mTrackedJobs.size(); i++) { final JobStatus js = mTrackedJobs.valueAt(i); final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i); for (int j = 0; j < jobs.size(); j++) { final JobStatus js = jobs.valueAt(j); if (!predicate.test(js)) { if (!predicate.test(js)) { continue; continue; } } final long jsToken = proto.start(StateControllerProto.ConnectivityController.TRACKED_JOBS); final long jsToken = proto.start( js.writeToShortProto(proto, StateControllerProto.ConnectivityController.TrackedJob.INFO); StateControllerProto.ConnectivityController.TRACKED_JOBS); js.writeToShortProto(proto, StateControllerProto.ConnectivityController.TrackedJob.INFO); proto.write(StateControllerProto.ConnectivityController.TrackedJob.SOURCE_UID, proto.write(StateControllerProto.ConnectivityController.TrackedJob.SOURCE_UID, js.getSourceUid()); js.getSourceUid()); NetworkRequest rn = js.getJob().getRequiredNetwork(); NetworkRequest rn = js.getJob().getRequiredNetwork(); if (rn != null) { if (rn != null) { rn.writeToProto(proto, rn.writeToProto(proto, StateControllerProto.ConnectivityController.TrackedJob.REQUIRED_NETWORK); StateControllerProto.ConnectivityController.TrackedJob .REQUIRED_NETWORK); } } proto.end(jsToken); proto.end(jsToken); } } } proto.end(mToken); proto.end(mToken); proto.end(token); proto.end(token); Loading