1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.job.controllers;
18 
19 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
20 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
21 import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
22 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
23 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
24 import static android.net.NetworkCapabilities.TRANSPORT_SATELLITE;
25 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
26 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
27 
28 import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
29 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
30 import static com.android.server.job.Flags.FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER;
31 
32 import android.annotation.NonNull;
33 import android.annotation.Nullable;
34 import android.app.ActivityManager;
35 import android.app.job.JobInfo;
36 import android.content.pm.PackageManager;
37 import android.net.ConnectivityManager;
38 import android.net.ConnectivityManager.NetworkCallback;
39 import android.net.INetworkPolicyListener;
40 import android.net.Network;
41 import android.net.NetworkCapabilities;
42 import android.net.NetworkPolicyManager;
43 import android.net.NetworkRequest;
44 import android.os.Handler;
45 import android.os.Looper;
46 import android.os.Message;
47 import android.os.UserHandle;
48 import android.provider.DeviceConfig;
49 import android.telephony.CellSignalStrength;
50 import android.telephony.SignalStrength;
51 import android.telephony.TelephonyCallback;
52 import android.telephony.TelephonyManager;
53 import android.text.format.DateUtils;
54 import android.util.ArrayMap;
55 import android.util.ArraySet;
56 import android.util.IndentingPrintWriter;
57 import android.util.Log;
58 import android.util.Pools;
59 import android.util.Slog;
60 import android.util.SparseArray;
61 import android.util.SparseBooleanArray;
62 import android.util.SparseIntArray;
63 import android.util.TimeUtils;
64 import android.util.proto.ProtoOutputStream;
65 
66 import com.android.internal.annotations.GuardedBy;
67 import com.android.internal.annotations.VisibleForTesting;
68 import com.android.server.AppSchedulingModuleThread;
69 import com.android.server.LocalServices;
70 import com.android.server.job.Flags;
71 import com.android.server.job.JobSchedulerService;
72 import com.android.server.job.JobSchedulerService.Constants;
73 import com.android.server.job.StateControllerProto;
74 import com.android.server.net.NetworkPolicyManagerInternal;
75 
76 import java.util.ArrayList;
77 import java.util.Comparator;
78 import java.util.List;
79 import java.util.Objects;
80 import java.util.Set;
81 import java.util.function.Predicate;
82 
83 /**
84  * Handles changes in connectivity.
85  * <p>
86  * Each app can have a different default networks or different connectivity
87  * status due to user-requested network policies, so we need to check
88  * constraints on a per-UID basis.
89  *
90  * Test: atest com.android.server.job.controllers.ConnectivityControllerTest
91  */
92 public final class ConnectivityController extends RestrictingController implements
93         ConnectivityManager.OnNetworkActiveListener {
94     private static final String TAG = "JobScheduler.Connectivity";
95     private static final boolean DEBUG = JobSchedulerService.DEBUG
96             || Log.isLoggable(TAG, Log.DEBUG);
97 
98     public static final long UNKNOWN_TIME = -1L;
99 
100     // The networking stack has a hard limit so we can't make this configurable.
101     private static final int MAX_NETWORK_CALLBACKS = 125;
102     /**
103      * Minimum amount of time that should have elapsed before we'll update a {@link UidStats}
104      * instance.
105      */
106     private static final long MIN_STATS_UPDATE_INTERVAL_MS = 30_000L;
107     private static final long MIN_ADJUST_CALLBACK_INTERVAL_MS = 1_000L;
108 
109     private static final int UNBYPASSABLE_BG_BLOCKED_REASONS =
110             ~ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND;
111     private static final int UNBYPASSABLE_EJ_BLOCKED_REASONS =
112             ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
113                     | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
114                     | ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND
115                     | ConnectivityManager.BLOCKED_REASON_DOZE);
116     private static final int UNBYPASSABLE_UI_BLOCKED_REASONS =
117             ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
118                     | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
119                     | ConnectivityManager.BLOCKED_REASON_DOZE
120                     | ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER
121                     | ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND
122                     | ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED);
123     private static final int UNBYPASSABLE_FOREGROUND_BLOCKED_REASONS =
124             ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
125                     | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
126                     | ConnectivityManager.BLOCKED_REASON_DOZE
127                     | ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER
128                     | ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND
129                     | ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED);
130 
131     @VisibleForTesting
132     static final int TRANSPORT_AFFINITY_UNDEFINED = 0;
133     @VisibleForTesting
134     static final int TRANSPORT_AFFINITY_PREFER = 1;
135     @VisibleForTesting
136     static final int TRANSPORT_AFFINITY_AVOID = 2;
137     /**
138      * Set of affinities to different network transports. If a given network has multiple
139      * transports, the avoided ones take priority --- a network with an avoided transport
140      * should be avoided if possible, even if the network has preferred transports as well.
141      */
142     @VisibleForTesting
143     static final SparseIntArray sNetworkTransportAffinities = new SparseIntArray();
144     static {
sNetworkTransportAffinities.put(TRANSPORT_CELLULAR, TRANSPORT_AFFINITY_AVOID)145         sNetworkTransportAffinities.put(TRANSPORT_CELLULAR, TRANSPORT_AFFINITY_AVOID);
sNetworkTransportAffinities.put(TRANSPORT_ETHERNET, TRANSPORT_AFFINITY_PREFER)146         sNetworkTransportAffinities.put(TRANSPORT_ETHERNET, TRANSPORT_AFFINITY_PREFER);
sNetworkTransportAffinities.put(TRANSPORT_SATELLITE, TRANSPORT_AFFINITY_AVOID)147         sNetworkTransportAffinities.put(TRANSPORT_SATELLITE, TRANSPORT_AFFINITY_AVOID);
sNetworkTransportAffinities.put(TRANSPORT_WIFI, TRANSPORT_AFFINITY_PREFER)148         sNetworkTransportAffinities.put(TRANSPORT_WIFI, TRANSPORT_AFFINITY_PREFER);
149     }
150 
151     private final CcConfig mCcConfig;
152     private final ConnectivityManager mConnManager;
153     private final NetworkPolicyManager mNetPolicyManager;
154     private final NetworkPolicyManagerInternal mNetPolicyManagerInternal;
155     private final FlexibilityController mFlexibilityController;
156 
157     /** List of tracked jobs keyed by source UID. */
158     @GuardedBy("mLock")
159     private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>();
160 
161     /**
162      * Keep track of all the UID's jobs that the controller has requested that NetworkPolicyManager
163      * grant an exception to in the app standby chain.
164      */
165     @GuardedBy("mLock")
166     private final SparseArray<ArraySet<JobStatus>> mRequestedWhitelistJobs = new SparseArray<>();
167 
168     /**
169      * Set of currently available networks mapped to their latest network capabilities. Cache the
170      * latest capabilities to avoid unnecessary calls into ConnectivityManager.
171      */
172     @GuardedBy("mLock")
173     private final ArrayMap<Network, CachedNetworkMetadata> mAvailableNetworks = new ArrayMap<>();
174 
175     @GuardedBy("mLock")
176     @Nullable
177     private Network mSystemDefaultNetwork;
178 
179     private final SparseArray<UidDefaultNetworkCallback> mCurrentDefaultNetworkCallbacks =
180             new SparseArray<>();
181     private final Comparator<UidStats> mUidStatsComparator = new Comparator<UidStats>() {
182         private int prioritizeExistenceOver(int threshold, int v1, int v2) {
183             // Check if they're both on the same side of the threshold.
184             if ((v1 > threshold && v2 > threshold) || (v1 <= threshold && v2 <= threshold)) {
185                 return 0;
186             }
187             // They're on opposite sides of the threshold.
188             if (v1 > threshold) {
189                 return -1;
190             }
191             return 1;
192         }
193 
194         @Override
195         public int compare(UidStats us1, UidStats us2) {
196             // Prioritize a UID ahead of another based on:
197             //   1. Already running connectivity jobs (so we don't drop the listener)
198             //   2. Waiting connectivity jobs would be ready with connectivity
199             //   3. An existing network satisfies a waiting connectivity job's requirements
200             //   4. TOP proc state
201             //   5. Existence of treat-as-UI UIJs (not just requested UIJs)
202             //   6. Existence of treat-as-EJ EJs (not just requested EJs)
203             //   7. FGS proc state
204             //   8. UIJ enqueue time
205             //   9. EJ enqueue time
206             //   10. Any other important job priorities/proc states
207             //   11. Enqueue time
208             // TODO: maybe consider number of jobs
209             // TODO: consider IMPORTANT_WHILE_FOREGROUND bit
210             final int runningPriority = prioritizeExistenceOver(0,
211                     us1.runningJobs.size(), us2.runningJobs.size());
212             if (runningPriority != 0) {
213                 return runningPriority;
214             }
215             // Prioritize any UIDs that have jobs that would be ready ahead of UIDs that don't.
216             final int readyWithConnPriority = prioritizeExistenceOver(0,
217                     us1.numReadyWithConnectivity, us2.numReadyWithConnectivity);
218             if (readyWithConnPriority != 0) {
219                 return readyWithConnPriority;
220             }
221             // They both have jobs that would be ready. Prioritize the UIDs whose requested
222             // network is available ahead of UIDs that don't have their requested network available.
223             final int reqAvailPriority = prioritizeExistenceOver(0,
224                     us1.numRequestedNetworkAvailable, us2.numRequestedNetworkAvailable);
225             if (reqAvailPriority != 0) {
226                 return reqAvailPriority;
227             }
228             // Prioritize the top app. If neither are top apps, then use a later prioritization
229             // check.
230             final int topPriority = prioritizeExistenceOver(JobInfo.BIAS_TOP_APP - 1,
231                     us1.baseBias, us2.baseBias);
232             if (topPriority != 0) {
233                 return topPriority;
234             }
235             // They're either both TOP or both not TOP. Prioritize the app that has runnable UIJs
236             // pending.
237             final int uijPriority = prioritizeExistenceOver(0, us1.numUIJs, us2.numUIJs);
238             if (uijPriority != 0) {
239                 return uijPriority;
240             }
241             // Still equivalent. Prioritize the app that has runnable EJs pending.
242             final int ejPriority = prioritizeExistenceOver(0, us1.numEJs, us2.numEJs);
243             if (ejPriority != 0) {
244                 return ejPriority;
245             }
246             // They both have runnable EJs.
247             // Prioritize an FGS+ app. If neither are FGS+ apps, then use a later prioritization
248             // check.
249             final int fgsPriority = prioritizeExistenceOver(JobInfo.BIAS_FOREGROUND_SERVICE - 1,
250                     us1.baseBias, us2.baseBias);
251             if (fgsPriority != 0) {
252                 return fgsPriority;
253             }
254             // Order them by UIJ enqueue time to help provide low UIJ latency.
255             if (us1.earliestUIJEnqueueTime < us2.earliestUIJEnqueueTime) {
256                 return -1;
257             } else if (us1.earliestUIJEnqueueTime > us2.earliestUIJEnqueueTime) {
258                 return 1;
259             }
260             // Order them by EJ enqueue time to help provide low EJ latency.
261             if (us1.earliestEJEnqueueTime < us2.earliestEJEnqueueTime) {
262                 return -1;
263             } else if (us1.earliestEJEnqueueTime > us2.earliestEJEnqueueTime) {
264                 return 1;
265             }
266             // Order by any latent important proc states.
267             if (us1.baseBias != us2.baseBias) {
268                 return us2.baseBias - us1.baseBias;
269             }
270             // Order by enqueue time.
271             if (us1.earliestEnqueueTime < us2.earliestEnqueueTime) {
272                 return -1;
273             }
274             return us1.earliestEnqueueTime > us2.earliestEnqueueTime ? 1 : 0;
275         }
276     };
277     private final SparseArray<UidStats> mUidStats = new SparseArray<>();
278     private final Pools.Pool<UidDefaultNetworkCallback> mDefaultNetworkCallbackPool =
279             new Pools.SimplePool<>(MAX_NETWORK_CALLBACKS);
280     /**
281      * List of UidStats, sorted by priority as defined in {@link #mUidStatsComparator}. The sorting
282      * is only done in {@link #maybeAdjustRegisteredCallbacksLocked()} and may sometimes be stale.
283      */
284     private final List<UidStats> mSortedStats = new ArrayList<>();
285     @GuardedBy("mLock")
286     private final SparseBooleanArray mBackgroundMeteredAllowed = new SparseBooleanArray();
287     @GuardedBy("mLock")
288     private long mLastCallbackAdjustmentTimeElapsed;
289     @GuardedBy("mLock")
290     private final SparseArray<CellSignalStrengthCallback> mSignalStrengths = new SparseArray<>();
291 
292     @GuardedBy("mLock")
293     private long mLastAllJobUpdateTimeElapsed;
294 
295     private static final int MSG_ADJUST_CALLBACKS = 0;
296     private static final int MSG_UPDATE_ALL_TRACKED_JOBS = 1;
297     private static final int MSG_DATA_SAVER_TOGGLED = 2;
298     private static final int MSG_UID_POLICIES_CHANGED = 3;
299     private static final int MSG_PROCESS_ACTIVE_NETWORK = 4;
300 
301     private final Handler mHandler;
302 
ConnectivityController(JobSchedulerService service, @NonNull FlexibilityController flexibilityController)303     public ConnectivityController(JobSchedulerService service,
304             @NonNull FlexibilityController flexibilityController) {
305         super(service);
306         mHandler = new CcHandler(AppSchedulingModuleThread.get().getLooper());
307         mCcConfig = new CcConfig();
308 
309         mConnManager = mContext.getSystemService(ConnectivityManager.class);
310         mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
311         mNetPolicyManagerInternal = LocalServices.getService(NetworkPolicyManagerInternal.class);
312         mFlexibilityController = flexibilityController;
313 
314         // We're interested in all network changes; internally we match these
315         // network changes against the active network for each UID with jobs.
316         final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
317         mConnManager.registerNetworkCallback(request, mNetworkCallback);
318 
319         mNetPolicyManager.registerListener(mNetPolicyListener);
320 
321         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
322             // For now, we don't have network affinities on watches.
323             sNetworkTransportAffinities.clear();
324         }
325     }
326 
327     @Override
startTrackingLocked()328     public void startTrackingLocked() {
329         if (Flags.batchConnectivityJobsPerNetwork()) {
330             mConnManager.registerSystemDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
331             mConnManager.addDefaultNetworkActiveListener(this);
332         }
333     }
334 
335     @GuardedBy("mLock")
336     @Override
maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob)337     public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
338         if (jobStatus.hasConnectivityConstraint()) {
339             final UidStats uidStats =
340                     getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), false);
341             if (wouldBeReadyWithConstraintLocked(jobStatus, JobStatus.CONSTRAINT_CONNECTIVITY)) {
342                 uidStats.numReadyWithConnectivity++;
343             }
344             ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUid());
345             if (jobs == null) {
346                 jobs = new ArraySet<>();
347                 mTrackedJobs.put(jobStatus.getSourceUid(), jobs);
348             }
349             jobs.add(jobStatus);
350             jobStatus.setTrackingController(JobStatus.TRACKING_CONNECTIVITY);
351             updateConstraintsSatisfied(jobStatus);
352         }
353     }
354 
355     @GuardedBy("mLock")
356     @Override
prepareForExecutionLocked(JobStatus jobStatus)357     public void prepareForExecutionLocked(JobStatus jobStatus) {
358         if (jobStatus.hasConnectivityConstraint()) {
359             final UidStats uidStats =
360                     getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
361             uidStats.runningJobs.add(jobStatus);
362         }
363     }
364 
365     @GuardedBy("mLock")
366     @Override
unprepareFromExecutionLocked(JobStatus jobStatus)367     public void unprepareFromExecutionLocked(JobStatus jobStatus) {
368         if (jobStatus.hasConnectivityConstraint()) {
369             final UidStats uidStats =
370                     getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
371             uidStats.runningJobs.remove(jobStatus);
372             postAdjustCallbacks();
373         }
374     }
375 
376     @GuardedBy("mLock")
377     @Override
maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob)378     public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) {
379         if (jobStatus.clearTrackingController(JobStatus.TRACKING_CONNECTIVITY)) {
380             ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUid());
381             if (jobs != null) {
382                 jobs.remove(jobStatus);
383             }
384             final UidStats uidStats =
385                     getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
386             uidStats.numReadyWithConnectivity--;
387             uidStats.runningJobs.remove(jobStatus);
388             maybeRevokeStandbyExceptionLocked(jobStatus);
389             postAdjustCallbacks();
390         }
391     }
392 
393     @Override
startTrackingRestrictedJobLocked(JobStatus jobStatus)394     public void startTrackingRestrictedJobLocked(JobStatus jobStatus) {
395         // Don't need to start tracking the job. If the job needed network, it would already be
396         // tracked.
397         if (jobStatus.hasConnectivityConstraint()) {
398             updateConstraintsSatisfied(jobStatus);
399         }
400     }
401 
402     @Override
stopTrackingRestrictedJobLocked(JobStatus jobStatus)403     public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) {
404         // Shouldn't stop tracking the job here. If the job was tracked, it still needs network,
405         // even after being unrestricted.
406         if (jobStatus.hasConnectivityConstraint()) {
407             updateConstraintsSatisfied(jobStatus);
408         }
409     }
410 
411     @NonNull
getUidStats(int uid, String packageName, boolean shouldExist)412     private UidStats getUidStats(int uid, String packageName, boolean shouldExist) {
413         UidStats us = mUidStats.get(uid);
414         if (us == null) {
415             if (shouldExist) {
416                 // This shouldn't be happening. We create a UidStats object for the app when the
417                 // first job is scheduled in maybeStartTrackingJobLocked() and only ever drop the
418                 // object if the app is uninstalled or the user is removed. That means that if we
419                 // end up in this situation, onAppRemovedLocked() or onUserRemovedLocked() was
420                 // called before maybeStopTrackingJobLocked(), which is the reverse order of what
421                 // JobSchedulerService does (JSS calls maybeStopTrackingJobLocked() for all jobs
422                 // before calling onAppRemovedLocked() or onUserRemovedLocked()).
423                 Slog.wtfStack(TAG,
424                         "UidStats was null after job for " + packageName + " was registered");
425             }
426             us = new UidStats(uid);
427             mUidStats.append(uid, us);
428         }
429         return us;
430     }
431 
432     /**
433      * Returns true if the job's requested network is available. This DOES NOT necessarily mean
434      * that the UID has been granted access to the network.
435      */
isNetworkAvailable(JobStatus job)436     public boolean isNetworkAvailable(JobStatus job) {
437         synchronized (mLock) {
438             for (int i = 0; i < mAvailableNetworks.size(); ++i) {
439                 final Network network = mAvailableNetworks.keyAt(i);
440                 final CachedNetworkMetadata metadata = mAvailableNetworks.valueAt(i);
441                 final NetworkCapabilities capabilities =
442                         metadata == null ? null : metadata.networkCapabilities;
443                 final boolean satisfied = isSatisfied(job, network, capabilities, mConstants);
444                 if (DEBUG) {
445                     Slog.v(TAG, "isNetworkAvailable(" + job + ") with network " + network
446                             + " and capabilities " + capabilities + ". Satisfied=" + satisfied);
447                 }
448                 if (satisfied) {
449                     return true;
450                 }
451             }
452             return false;
453         }
454     }
455 
456     /**
457      * Request that NetworkPolicyManager grant an exception to the uid from its standby policy
458      * chain.
459      */
460     @VisibleForTesting
461     @GuardedBy("mLock")
requestStandbyExceptionLocked(JobStatus job)462     void requestStandbyExceptionLocked(JobStatus job) {
463         final int uid = job.getSourceUid();
464         // Need to call this before adding the job.
465         final boolean isExceptionRequested = isStandbyExceptionRequestedLocked(uid);
466         ArraySet<JobStatus> jobs = mRequestedWhitelistJobs.get(uid);
467         if (jobs == null) {
468             jobs = new ArraySet<JobStatus>();
469             mRequestedWhitelistJobs.put(uid, jobs);
470         }
471         if (!jobs.add(job) || isExceptionRequested) {
472             if (DEBUG) {
473                 Slog.i(TAG, "requestStandbyExceptionLocked found exception already requested.");
474             }
475             return;
476         }
477         if (DEBUG) Slog.i(TAG, "Requesting standby exception for UID: " + uid);
478         mNetPolicyManagerInternal.setAppIdleWhitelist(uid, true);
479     }
480 
481     /** Returns whether a standby exception has been requested for the UID. */
482     @VisibleForTesting
483     @GuardedBy("mLock")
isStandbyExceptionRequestedLocked(final int uid)484     boolean isStandbyExceptionRequestedLocked(final int uid) {
485         ArraySet jobs = mRequestedWhitelistJobs.get(uid);
486         return jobs != null && jobs.size() > 0;
487     }
488 
489     /**
490      * Tell NetworkPolicyManager not to block a UID's network connection if that's the only
491      * thing stopping a job from running.
492      */
493     @GuardedBy("mLock")
494     @Override
evaluateStateLocked(JobStatus jobStatus)495     public void evaluateStateLocked(JobStatus jobStatus) {
496         if (!jobStatus.hasConnectivityConstraint()) {
497             return;
498         }
499 
500         final UidStats uidStats =
501                 getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
502 
503         if (jobStatus.shouldTreatAsExpeditedJob() || jobStatus.shouldTreatAsUserInitiatedJob()) {
504             if (!jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY)) {
505                 // Don't request a direct hole through any of the firewalls. Instead, mark the
506                 // constraint as satisfied if the network is available, and the job will get
507                 // through the firewalls once it starts running and the proc state is elevated.
508                 // This is the same behavior that FGS see.
509                 updateConstraintsSatisfied(jobStatus);
510             }
511             // Don't need to update constraint here if the network goes away. We'll do that as part
512             // of regular processing when we're notified about the drop.
513         } else if (((jobStatus.isRequestedExpeditedJob() && !jobStatus.shouldTreatAsExpeditedJob())
514                 || (jobStatus.getJob().isUserInitiated()
515                         && !jobStatus.shouldTreatAsUserInitiatedJob()))
516                 && jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY)) {
517             // Make sure we don't accidentally keep the constraint as satisfied if the job went
518             // from being expedited-ready to not-expeditable.
519             updateConstraintsSatisfied(jobStatus);
520         }
521 
522         // Always check the full job readiness stat in case the component has been disabled.
523         if (wouldBeReadyWithConstraintLocked(jobStatus, JobStatus.CONSTRAINT_CONNECTIVITY)
524                 && isNetworkAvailable(jobStatus)) {
525             if (DEBUG) {
526                 Slog.i(TAG, "evaluateStateLocked finds job " + jobStatus + " would be ready.");
527             }
528             uidStats.numReadyWithConnectivity++;
529             requestStandbyExceptionLocked(jobStatus);
530         } else {
531             if (DEBUG) {
532                 Slog.i(TAG, "evaluateStateLocked finds job " + jobStatus + " would not be ready.");
533             }
534             // Don't decrement numReadyWithConnectivity here because we don't know if it was
535             // incremented for this job. The count will be set properly in
536             // maybeAdjustRegisteredCallbacksLocked().
537             maybeRevokeStandbyExceptionLocked(jobStatus);
538         }
539     }
540 
541     @GuardedBy("mLock")
542     @Override
reevaluateStateLocked(final int uid)543     public void reevaluateStateLocked(final int uid) {
544         // Check if we still need a connectivity exception in case the JobService was disabled.
545         ArraySet<JobStatus> jobs = mTrackedJobs.get(uid);
546         if (jobs == null) {
547             return;
548         }
549         for (int i = jobs.size() - 1; i >= 0; i--) {
550             evaluateStateLocked(jobs.valueAt(i));
551         }
552     }
553 
554     /** Cancel the requested standby exception if none of the jobs would be ready to run anyway. */
555     @VisibleForTesting
556     @GuardedBy("mLock")
maybeRevokeStandbyExceptionLocked(final JobStatus job)557     void maybeRevokeStandbyExceptionLocked(final JobStatus job) {
558         final int uid = job.getSourceUid();
559         if (!isStandbyExceptionRequestedLocked(uid)) {
560             return;
561         }
562         ArraySet<JobStatus> jobs = mRequestedWhitelistJobs.get(uid);
563         if (jobs == null) {
564             Slog.wtf(TAG,
565                     "maybeRevokeStandbyExceptionLocked found null jobs array even though a "
566                             + "standby exception has been requested.");
567             return;
568         }
569         if (!jobs.remove(job) || jobs.size() > 0) {
570             if (DEBUG) {
571                 Slog.i(TAG,
572                         "maybeRevokeStandbyExceptionLocked not revoking because there are still "
573                                 + jobs.size() + " jobs left.");
574             }
575             return;
576         }
577         // No more jobs that need an exception.
578         revokeStandbyExceptionLocked(uid);
579     }
580 
581     /**
582      * Tell NetworkPolicyManager to revoke any exception it granted from its standby policy chain
583      * for the uid.
584      */
585     @GuardedBy("mLock")
revokeStandbyExceptionLocked(final int uid)586     private void revokeStandbyExceptionLocked(final int uid) {
587         if (DEBUG) Slog.i(TAG, "Revoking standby exception for UID: " + uid);
588         mNetPolicyManagerInternal.setAppIdleWhitelist(uid, false);
589         mRequestedWhitelistJobs.remove(uid);
590     }
591 
592     @GuardedBy("mLock")
593     @Override
onAppRemovedLocked(String pkgName, int uid)594     public void onAppRemovedLocked(String pkgName, int uid) {
595         if (mService.getPackagesForUidLocked(uid) == null) {
596             // All packages in the UID have been removed. It's safe to remove things based on
597             // UID alone.
598             mTrackedJobs.delete(uid);
599             mBackgroundMeteredAllowed.delete(uid);
600             UidStats uidStats = mUidStats.removeReturnOld(uid);
601             unregisterDefaultNetworkCallbackLocked(uid, sElapsedRealtimeClock.millis());
602             mSortedStats.remove(uidStats);
603             registerPendingUidCallbacksLocked();
604         }
605     }
606 
607     @GuardedBy("mLock")
608     @Override
onUserRemovedLocked(int userId)609     public void onUserRemovedLocked(int userId) {
610         final long nowElapsed = sElapsedRealtimeClock.millis();
611         for (int u = mUidStats.size() - 1; u >= 0; --u) {
612             UidStats uidStats = mUidStats.valueAt(u);
613             if (UserHandle.getUserId(uidStats.uid) == userId) {
614                 unregisterDefaultNetworkCallbackLocked(uidStats.uid, nowElapsed);
615                 mSortedStats.remove(uidStats);
616                 mUidStats.removeAt(u);
617             }
618         }
619         for (int u = mBackgroundMeteredAllowed.size() - 1; u >= 0; --u) {
620             final int uid = mBackgroundMeteredAllowed.keyAt(u);
621             if (UserHandle.getUserId(uid) == userId) {
622                 mBackgroundMeteredAllowed.removeAt(u);
623             }
624         }
625         postAdjustCallbacks();
626     }
627 
628     @GuardedBy("mLock")
629     @Override
onUidBiasChangedLocked(int uid, int prevBias, int newBias)630     public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
631         UidStats uidStats = mUidStats.get(uid);
632         if (uidStats != null && uidStats.baseBias != newBias) {
633             uidStats.baseBias = newBias;
634             postAdjustCallbacks();
635         }
636     }
637 
638     @Override
639     @GuardedBy("mLock")
onBatteryStateChangedLocked()640     public void onBatteryStateChangedLocked() {
641         // Update job bookkeeping out of band to avoid blocking broadcast progress.
642         mHandler.sendEmptyMessage(MSG_UPDATE_ALL_TRACKED_JOBS);
643     }
644 
645     @Override
prepareForUpdatedConstantsLocked()646     public void prepareForUpdatedConstantsLocked() {
647         mCcConfig.mShouldReprocessNetworkCapabilities = false;
648         mCcConfig.mFlexIsEnabled = mFlexibilityController.isEnabled();
649     }
650 
651     @Override
processConstantLocked(@onNull DeviceConfig.Properties properties, @NonNull String key)652     public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
653             @NonNull String key) {
654         mCcConfig.processConstantLocked(properties, key);
655     }
656 
657     @Override
onConstantsUpdatedLocked()658     public void onConstantsUpdatedLocked() {
659         if (mCcConfig.mShouldReprocessNetworkCapabilities
660                 || (mFlexibilityController.isEnabled() != mCcConfig.mFlexIsEnabled)) {
661             AppSchedulingModuleThread.getHandler().post(() -> {
662                 boolean flexAffinitiesChanged = false;
663                 boolean flexAffinitiesSatisfied = false;
664                 synchronized (mLock) {
665                     for (int i = 0; i < mAvailableNetworks.size(); ++i) {
666                         CachedNetworkMetadata metadata = mAvailableNetworks.valueAt(i);
667                         if (metadata == null) {
668                             continue;
669                         }
670                         if (updateTransportAffinitySatisfaction(metadata)) {
671                             // Something changed. Update jobs.
672                             flexAffinitiesChanged = true;
673                         }
674                         flexAffinitiesSatisfied |= metadata.satisfiesTransportAffinities;
675                     }
676                     if (flexAffinitiesChanged) {
677                         mFlexibilityController.setConstraintSatisfied(
678                                 JobStatus.CONSTRAINT_CONNECTIVITY,
679                                 flexAffinitiesSatisfied, sElapsedRealtimeClock.millis());
680                         updateAllTrackedJobsLocked(false);
681                     }
682                 }
683             });
684         }
685     }
686 
isUsable(NetworkCapabilities capabilities)687     private boolean isUsable(NetworkCapabilities capabilities) {
688         return capabilities != null
689                 && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
690     }
691 
692     /**
693      * Test to see if running the given job on the given network is insane.
694      * <p>
695      * For example, if a job is trying to send 10MB over a 128Kbps EDGE
696      * connection, it would take 10.4 minutes, and has no chance of succeeding
697      * before the job times out, so we'd be insane to try running it.
698      */
isInsane(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants)699     private boolean isInsane(JobStatus jobStatus, Network network,
700             NetworkCapabilities capabilities, Constants constants) {
701         // Use the maximum possible time since it gives us an upper bound, even though the job
702         // could end up stopping earlier.
703         final long maxJobExecutionTimeMs = mService.getMaxJobExecutionTimeMs(jobStatus);
704 
705         final long minimumChunkBytes = jobStatus.getMinimumNetworkChunkBytes();
706         if (minimumChunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
707             final long bandwidthDown = capabilities.getLinkDownstreamBandwidthKbps();
708             // If we don't know the bandwidth, all we can do is hope the job finishes the minimum
709             // chunk in time.
710             if (bandwidthDown > 0) {
711                 final long estimatedMillis =
712                         calculateTransferTimeMs(minimumChunkBytes, bandwidthDown);
713                 if (estimatedMillis > maxJobExecutionTimeMs) {
714                     // If we'd never finish the minimum chunk before the timeout, we'd be insane!
715                     Slog.w(TAG, "Minimum chunk " + minimumChunkBytes + " bytes over "
716                             + bandwidthDown + " kbps network would take "
717                             + estimatedMillis + "ms and job has "
718                             + maxJobExecutionTimeMs + "ms to run; that's insane!");
719                     return true;
720                 }
721             }
722             final long bandwidthUp = capabilities.getLinkUpstreamBandwidthKbps();
723             // If we don't know the bandwidth, all we can do is hope the job finishes in time.
724             if (bandwidthUp > 0) {
725                 final long estimatedMillis =
726                         calculateTransferTimeMs(minimumChunkBytes, bandwidthUp);
727                 if (estimatedMillis > maxJobExecutionTimeMs) {
728                     // If we'd never finish the minimum chunk before the timeout, we'd be insane!
729                     Slog.w(TAG, "Minimum chunk " + minimumChunkBytes + " bytes over " + bandwidthUp
730                             + " kbps network would take " + estimatedMillis + "ms and job has "
731                             + maxJobExecutionTimeMs + "ms to run; that's insane!");
732                     return true;
733                 }
734             }
735             return false;
736         }
737 
738         // Minimum chunk size isn't defined. Check using the estimated upload/download sizes.
739 
740         if (capabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
741                 && mService.isBatteryCharging()) {
742             // We're charging and on an unmetered network. We don't have to be as conservative about
743             // making sure the job will run within its max execution time. Let's just hope the app
744             // supports interruptible work.
745             return false;
746         }
747 
748 
749         final long downloadBytes = jobStatus.getEstimatedNetworkDownloadBytes();
750         if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
751             final long bandwidth = capabilities.getLinkDownstreamBandwidthKbps();
752             // If we don't know the bandwidth, all we can do is hope the job finishes in time.
753             if (bandwidth > 0) {
754                 final long estimatedMillis = calculateTransferTimeMs(downloadBytes, bandwidth);
755                 if (estimatedMillis > maxJobExecutionTimeMs) {
756                     // If we'd never finish before the timeout, we'd be insane!
757                     Slog.w(TAG, "Estimated " + downloadBytes + " download bytes over " + bandwidth
758                             + " kbps network would take " + estimatedMillis + "ms and job has "
759                             + maxJobExecutionTimeMs + "ms to run; that's insane!");
760                     return true;
761                 }
762             }
763         }
764 
765         final long uploadBytes = jobStatus.getEstimatedNetworkUploadBytes();
766         if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
767             final long bandwidth = capabilities.getLinkUpstreamBandwidthKbps();
768             // If we don't know the bandwidth, all we can do is hope the job finishes in time.
769             if (bandwidth > 0) {
770                 final long estimatedMillis = calculateTransferTimeMs(uploadBytes, bandwidth);
771                 if (estimatedMillis > maxJobExecutionTimeMs) {
772                     // If we'd never finish before the timeout, we'd be insane!
773                     Slog.w(TAG, "Estimated " + uploadBytes + " upload bytes over " + bandwidth
774                             + " kbps network would take " + estimatedMillis + "ms and job has "
775                             + maxJobExecutionTimeMs + "ms to run; that's insane!");
776                     return true;
777                 }
778             }
779         }
780 
781         return false;
782     }
783 
isMeteredAllowed(@onNull JobStatus jobStatus, @NonNull NetworkCapabilities networkCapabilities)784     private boolean isMeteredAllowed(@NonNull JobStatus jobStatus,
785             @NonNull NetworkCapabilities networkCapabilities) {
786         // Network isn't metered. Usage is allowed. The rest of this method doesn't apply.
787         if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
788                 || networkCapabilities.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)) {
789             return true;
790         }
791 
792         final int uid = jobStatus.getSourceUid();
793         final int procState = mService.getUidProcState(uid);
794         final int capabilities = mService.getUidCapabilities(uid);
795         // Jobs don't raise the proc state to anything better than IMPORTANT_FOREGROUND.
796         // If the app is in a better state, see if it has the capability to use the metered network.
797         final boolean currentStateAllows = procState != ActivityManager.PROCESS_STATE_UNKNOWN
798                 && procState < ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
799                 && NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground(
800                         procState, capabilities);
801         if (DEBUG) {
802             Slog.d(TAG, "UID " + uid
803                     + " current state allows metered network=" + currentStateAllows
804                     + " procState=" + ActivityManager.procStateToString(procState)
805                     + " capabilities=" + ActivityManager.getCapabilitiesSummary(capabilities));
806         }
807         if (currentStateAllows) {
808             return true;
809         }
810 
811         if ((jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
812             final int expectedProcState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
813             final int mergedCapabilities = capabilities
814                     | NetworkPolicyManager.getDefaultProcessNetworkCapabilities(expectedProcState);
815             final boolean wouldBeAllowed =
816                     NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground(
817                             expectedProcState, mergedCapabilities);
818             if (DEBUG) {
819                 Slog.d(TAG, "UID " + uid
820                         + " willBeForeground flag allows metered network=" + wouldBeAllowed
821                         + " capabilities="
822                         + ActivityManager.getCapabilitiesSummary(mergedCapabilities));
823             }
824             if (wouldBeAllowed) {
825                 return true;
826             }
827         }
828 
829         if (jobStatus.shouldTreatAsUserInitiatedJob()) {
830             // Since the job is initiated by the user and will be visible to the user, it
831             // should be able to run on metered networks, similar to FGS.
832             // With user-initiated jobs, JobScheduler will request that the process
833             // run at IMPORTANT_FOREGROUND process state
834             // and get the USER_RESTRICTED_NETWORK process capability.
835             final int expectedProcState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
836             final int mergedCapabilities = capabilities
837                     | ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK
838                     | NetworkPolicyManager.getDefaultProcessNetworkCapabilities(expectedProcState);
839             final boolean wouldBeAllowed =
840                     NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground(
841                             expectedProcState, mergedCapabilities);
842             if (DEBUG) {
843                 Slog.d(TAG, "UID " + uid
844                         + " UI job state allows metered network=" + wouldBeAllowed
845                         + " capabilities=" + mergedCapabilities);
846             }
847             if (wouldBeAllowed) {
848                 return true;
849             }
850         }
851 
852         if (mBackgroundMeteredAllowed.indexOfKey(uid) >= 0) {
853             return mBackgroundMeteredAllowed.get(uid);
854         }
855 
856         final boolean allowed =
857                 mNetPolicyManager.getRestrictBackgroundStatus(uid)
858                         != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
859         if (DEBUG) {
860             Slog.d(TAG, "UID " + uid + " allowed in data saver=" + allowed);
861         }
862         mBackgroundMeteredAllowed.put(uid, allowed);
863         return allowed;
864     }
865 
866     /**
867      * Return the estimated amount of time this job will be transferring data,
868      * based on the current network speed.
869      */
getEstimatedTransferTimeMs(JobStatus jobStatus)870     public long getEstimatedTransferTimeMs(JobStatus jobStatus) {
871         final long downloadBytes = jobStatus.getEstimatedNetworkDownloadBytes();
872         final long uploadBytes = jobStatus.getEstimatedNetworkUploadBytes();
873         if (downloadBytes == JobInfo.NETWORK_BYTES_UNKNOWN
874                 && uploadBytes == JobInfo.NETWORK_BYTES_UNKNOWN) {
875             return UNKNOWN_TIME;
876         }
877         if (jobStatus.network == null) {
878             // This job doesn't have a network assigned.
879             return UNKNOWN_TIME;
880         }
881         NetworkCapabilities capabilities = getNetworkCapabilities(jobStatus.network);
882         if (capabilities == null) {
883             return UNKNOWN_TIME;
884         }
885         final long estimatedDownloadTimeMs = calculateTransferTimeMs(downloadBytes,
886                 capabilities.getLinkDownstreamBandwidthKbps());
887         final long estimatedUploadTimeMs = calculateTransferTimeMs(uploadBytes,
888                 capabilities.getLinkUpstreamBandwidthKbps());
889         if (estimatedDownloadTimeMs == UNKNOWN_TIME) {
890             return estimatedUploadTimeMs;
891         } else if (estimatedUploadTimeMs == UNKNOWN_TIME) {
892             return estimatedDownloadTimeMs;
893         }
894         return estimatedDownloadTimeMs + estimatedUploadTimeMs;
895     }
896 
897     @VisibleForTesting
calculateTransferTimeMs(long transferBytes, long bandwidthKbps)898     static long calculateTransferTimeMs(long transferBytes, long bandwidthKbps) {
899         if (transferBytes == JobInfo.NETWORK_BYTES_UNKNOWN || bandwidthKbps <= 0) {
900             return UNKNOWN_TIME;
901         }
902         return (transferBytes * DateUtils.SECOND_IN_MILLIS)
903                 // Multiply by 1000 to convert kilobits to bits.
904                 // Divide by 8 to convert bits to bytes.
905                 / (bandwidthKbps * 1000 / 8);
906     }
907 
isCongestionDelayed(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants)908     private static boolean isCongestionDelayed(JobStatus jobStatus, Network network,
909             NetworkCapabilities capabilities, Constants constants) {
910         // If network is congested, and job is less than 50% through the
911         // developer-requested window, then we're okay delaying the job.
912         if (!capabilities.hasCapability(NET_CAPABILITY_NOT_CONGESTED)) {
913             return jobStatus.getFractionRunTime() < constants.CONN_CONGESTION_DELAY_FRAC;
914         } else {
915             return false;
916         }
917     }
918 
919     @GuardedBy("mLock")
isStrongEnough(JobStatus jobStatus, NetworkCapabilities capabilities, Constants constants)920     private boolean isStrongEnough(JobStatus jobStatus, NetworkCapabilities capabilities,
921             Constants constants) {
922         final int priority = jobStatus.getEffectivePriority();
923         if (priority >= JobInfo.PRIORITY_HIGH) {
924             return true;
925         }
926         if (!constants.CONN_USE_CELL_SIGNAL_STRENGTH) {
927             return true;
928         }
929         if (!capabilities.hasTransport(TRANSPORT_CELLULAR)) {
930             return true;
931         }
932         if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
933             // VPNs may have multiple underlying networks and determining the correct strength
934             // may not be straightforward.
935             // Transmitting data over a VPN is generally more battery-expensive than on the
936             // underlying network, so:
937             // TODO: find a good way to reduce job use of VPN when it'll be very expensive
938             // For now, we just pretend VPNs are always strong enough
939             return true;
940         }
941 
942         // VCNs running over WiFi will declare TRANSPORT_CELLULAR. When connected, a VCN will
943         // most likely be the default network. We ideally don't want this to restrict jobs when the
944         // VCN incorrectly declares the CELLULAR transport, but there's currently no way to
945         // determine if a network is a VCN. When there is:
946         // TODO(216127782): exclude VCN running over WiFi from this check
947 
948         int signalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
949         // Use the best strength found.
950         final Set<Integer> subscriptionIds = capabilities.getSubscriptionIds();
951         for (int subId : subscriptionIds) {
952             CellSignalStrengthCallback callback = mSignalStrengths.get(subId);
953             if (callback != null) {
954                 signalStrength = Math.max(signalStrength, callback.signalStrength);
955             } else {
956                 Slog.wtf(TAG,
957                         "Subscription ID " + subId + " doesn't have a registered callback");
958             }
959         }
960         if (DEBUG) {
961             Slog.d(TAG, "Cell signal strength for job=" + signalStrength);
962         }
963         // Treat "NONE_OR_UNKNOWN" as "NONE".
964         if (signalStrength <= CellSignalStrength.SIGNAL_STRENGTH_POOR) {
965             // If signal strength is poor, don't run MIN or LOW priority jobs, and only
966             // run DEFAULT priority jobs if the device is charging or the job has been waiting
967             // long enough.
968             if (priority > JobInfo.PRIORITY_DEFAULT) {
969                 return true;
970             }
971             if (priority < JobInfo.PRIORITY_DEFAULT) {
972                 return false;
973             }
974             // DEFAULT job.
975             return (mService.isBatteryCharging() && mService.isBatteryNotLow())
976                     || jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC;
977         }
978         if (signalStrength <= CellSignalStrength.SIGNAL_STRENGTH_MODERATE) {
979             // If signal strength is moderate, only run MIN priority jobs when the device
980             // is charging, or the job is already running.
981             if (priority >= JobInfo.PRIORITY_LOW) {
982                 return true;
983             }
984             // MIN job.
985             if (mService.isBatteryCharging() && mService.isBatteryNotLow()) {
986                 return true;
987             }
988             final UidStats uidStats = getUidStats(
989                     jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
990             return uidStats.runningJobs.contains(jobStatus);
991         }
992         return true;
993     }
994 
copyCapabilities( @onNull final NetworkRequest request)995     private static NetworkCapabilities.Builder copyCapabilities(
996             @NonNull final NetworkRequest request) {
997         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
998         for (int transport : request.getTransportTypes()) builder.addTransportType(transport);
999         for (int capability : request.getCapabilities()) builder.addCapability(capability);
1000         return builder;
1001     }
1002 
isStrictSatisfied(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants)1003     private static boolean isStrictSatisfied(JobStatus jobStatus, Network network,
1004             NetworkCapabilities capabilities, Constants constants) {
1005         // A restricted job that's out of quota MUST use an unmetered network.
1006         if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX
1007                 && (!jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA))) {
1008             final NetworkCapabilities.Builder builder =
1009                     copyCapabilities(jobStatus.getJob().getRequiredNetwork());
1010             builder.addCapability(NET_CAPABILITY_NOT_METERED);
1011             return builder.build().satisfiedByNetworkCapabilities(capabilities);
1012         } else {
1013             return jobStatus.getJob().getRequiredNetwork().canBeSatisfiedBy(capabilities);
1014         }
1015     }
1016 
isRelaxedSatisfied(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants)1017     private boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
1018             NetworkCapabilities capabilities, Constants constants) {
1019         // Only consider doing this for unrestricted prefetching jobs
1020         if (!jobStatus.getJob().isPrefetch() || jobStatus.getStandbyBucket() == RESTRICTED_INDEX) {
1021             return false;
1022         }
1023         final long estDownloadBytes = jobStatus.getEstimatedNetworkDownloadBytes();
1024         if (estDownloadBytes <= 0) {
1025             // Need to at least know the estimated download bytes for a prefetch job.
1026             return false;
1027         }
1028         if (Flags.relaxPrefetchConnectivityConstraintOnlyOnCharger()) {
1029             // Since the constraint relaxation isn't required by the job, only do it when the
1030             // device is charging and the battery level is above the "low battery" threshold.
1031             if (!mService.isBatteryCharging() || !mService.isBatteryNotLow()) {
1032                 return false;
1033             }
1034         }
1035 
1036         // See if we match after relaxing any unmetered request
1037         final NetworkCapabilities.Builder builder =
1038                 copyCapabilities(jobStatus.getJob().getRequiredNetwork());
1039         builder.removeCapability(NET_CAPABILITY_NOT_METERED);
1040         if (builder.build().satisfiedByNetworkCapabilities(capabilities)
1041                 && jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC) {
1042             final long opportunisticQuotaBytes =
1043                     mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
1044                             network, NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS);
1045             final long estUploadBytes = jobStatus.getEstimatedNetworkUploadBytes();
1046             final long estimatedBytes = estDownloadBytes
1047                     + (estUploadBytes == JobInfo.NETWORK_BYTES_UNKNOWN ? 0 : estUploadBytes);
1048             return opportunisticQuotaBytes >= estimatedBytes;
1049         }
1050 
1051         return false;
1052     }
1053 
1054     @VisibleForTesting
isSatisfied(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants)1055     boolean isSatisfied(JobStatus jobStatus, Network network,
1056             NetworkCapabilities capabilities, Constants constants) {
1057         // Zeroth, we gotta have a network to think about being satisfied
1058         if (network == null || capabilities == null) return false;
1059 
1060         if (!isUsable(capabilities)) return false;
1061 
1062         // First, are we insane?
1063         if (isInsane(jobStatus, network, capabilities, constants)) return false;
1064 
1065         // User-initiated jobs might make NetworkPolicyManager open up network access for
1066         // the whole UID. If network access is opened up just because of UI jobs, we want
1067         // to make sure that non-UI jobs don't run during that time,
1068         // so make sure the job can make use of the metered network at this time.
1069         if (!isMeteredAllowed(jobStatus, capabilities)) return false;
1070 
1071         // Second, is the network congested?
1072         if (isCongestionDelayed(jobStatus, network, capabilities, constants)) return false;
1073 
1074         if (!isStrongEnough(jobStatus, capabilities, constants)) return false;
1075 
1076         // Is the network a strict match?
1077         if (isStrictSatisfied(jobStatus, network, capabilities, constants)) return true;
1078 
1079         // Is the network a relaxed match?
1080         if (isRelaxedSatisfied(jobStatus, network, capabilities, constants)) return true;
1081 
1082         return false;
1083     }
1084 
1085     /**
1086      * Updates {@link CachedNetworkMetadata#satisfiesTransportAffinities} in the given
1087      * {@link CachedNetworkMetadata} object.
1088      * @return true if the satisfaction changed
1089      */
updateTransportAffinitySatisfaction( @onNull CachedNetworkMetadata cachedNetworkMetadata)1090     private boolean updateTransportAffinitySatisfaction(
1091             @NonNull CachedNetworkMetadata cachedNetworkMetadata) {
1092         final boolean satisfiesAffinities =
1093                 satisfiesTransportAffinities(cachedNetworkMetadata.networkCapabilities);
1094         if (cachedNetworkMetadata.satisfiesTransportAffinities != satisfiesAffinities) {
1095             cachedNetworkMetadata.satisfiesTransportAffinities = satisfiesAffinities;
1096             return true;
1097         }
1098         return false;
1099     }
1100 
satisfiesTransportAffinities(@ullable NetworkCapabilities capabilities)1101     private boolean satisfiesTransportAffinities(@Nullable NetworkCapabilities capabilities) {
1102         if (!mFlexibilityController.isEnabled()) {
1103             return true;
1104         }
1105         if (capabilities == null) {
1106             Slog.wtf(TAG, "Network constraint satisfied with null capabilities");
1107             return !mCcConfig.AVOID_UNDEFINED_TRANSPORT_AFFINITY;
1108         }
1109 
1110         if (sNetworkTransportAffinities.size() == 0) {
1111             return !mCcConfig.AVOID_UNDEFINED_TRANSPORT_AFFINITY;
1112         }
1113 
1114         final int[] transports = capabilities.getTransportTypes();
1115         if (transports.length == 0) {
1116             return !mCcConfig.AVOID_UNDEFINED_TRANSPORT_AFFINITY;
1117         }
1118 
1119         for (int t : transports) {
1120             int affinity = sNetworkTransportAffinities.get(t, TRANSPORT_AFFINITY_UNDEFINED);
1121             if (DEBUG) {
1122                 Slog.d(TAG,
1123                         "satisfiesTransportAffinities transport=" + t + " aff=" + affinity);
1124             }
1125             switch (affinity) {
1126                 case TRANSPORT_AFFINITY_UNDEFINED:
1127                     if (mCcConfig.AVOID_UNDEFINED_TRANSPORT_AFFINITY) {
1128                         // Avoided transports take precedence.
1129                         // Return as soon as we encounter a transport to avoid.
1130                         return false;
1131                     }
1132                     break;
1133                 case TRANSPORT_AFFINITY_PREFER:
1134                     // Nothing to do here. We like this transport.
1135                     break;
1136                 case TRANSPORT_AFFINITY_AVOID:
1137                     // Avoided transports take precedence.
1138                     // Return as soon as we encounter a transport to avoid.
1139                     return false;
1140             }
1141         }
1142 
1143         // Didn't see any transport to avoid.
1144         return true;
1145     }
1146 
1147     @GuardedBy("mLock")
maybeRegisterDefaultNetworkCallbackLocked(JobStatus jobStatus)1148     private void maybeRegisterDefaultNetworkCallbackLocked(JobStatus jobStatus) {
1149         final int sourceUid = jobStatus.getSourceUid();
1150         if (mCurrentDefaultNetworkCallbacks.contains(sourceUid)) {
1151             return;
1152         }
1153         final UidStats uidStats =
1154                 getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
1155         if (!mSortedStats.contains(uidStats)) {
1156             mSortedStats.add(uidStats);
1157         }
1158         if (mCurrentDefaultNetworkCallbacks.size() >= MAX_NETWORK_CALLBACKS) {
1159             postAdjustCallbacks();
1160             return;
1161         }
1162         registerPendingUidCallbacksLocked();
1163     }
1164 
1165     /**
1166      * Register UID callbacks for UIDs that are next in line, based on the current order in {@link
1167      * #mSortedStats}. This assumes that there are only registered callbacks for UIDs in the top
1168      * {@value #MAX_NETWORK_CALLBACKS} UIDs and that the only UIDs missing callbacks are the lower
1169      * priority ones.
1170      */
1171     @GuardedBy("mLock")
registerPendingUidCallbacksLocked()1172     private void registerPendingUidCallbacksLocked() {
1173         final int numCallbacks = mCurrentDefaultNetworkCallbacks.size();
1174         final int numPending = mSortedStats.size();
1175         if (numPending < numCallbacks) {
1176             // This means there's a bug in the code >.<
1177             Slog.wtf(TAG, "There are more registered callbacks than sorted UIDs: "
1178                     + numCallbacks + " vs " + numPending);
1179         }
1180         for (int i = numCallbacks; i < numPending && i < MAX_NETWORK_CALLBACKS; ++i) {
1181             UidStats uidStats = mSortedStats.get(i);
1182             UidDefaultNetworkCallback callback = mDefaultNetworkCallbackPool.acquire();
1183             if (callback == null) {
1184                 callback = new UidDefaultNetworkCallback();
1185             }
1186             callback.setUid(uidStats.uid);
1187             mCurrentDefaultNetworkCallbacks.append(uidStats.uid, callback);
1188             mConnManager.registerDefaultNetworkCallbackForUid(uidStats.uid, callback, mHandler);
1189         }
1190     }
1191 
postAdjustCallbacks()1192     private void postAdjustCallbacks() {
1193         postAdjustCallbacks(0);
1194     }
1195 
postAdjustCallbacks(long delayMs)1196     private void postAdjustCallbacks(long delayMs) {
1197         mHandler.sendEmptyMessageDelayed(MSG_ADJUST_CALLBACKS, delayMs);
1198     }
1199 
1200     @GuardedBy("mLock")
maybeAdjustRegisteredCallbacksLocked()1201     private void maybeAdjustRegisteredCallbacksLocked() {
1202         mHandler.removeMessages(MSG_ADJUST_CALLBACKS);
1203 
1204         final int count = mUidStats.size();
1205         if (count == mCurrentDefaultNetworkCallbacks.size()) {
1206             // All of them are registered and there are no blocked UIDs.
1207             // No point evaluating all UIDs.
1208             return;
1209         }
1210 
1211         final long nowElapsed = sElapsedRealtimeClock.millis();
1212         if (nowElapsed - mLastCallbackAdjustmentTimeElapsed < MIN_ADJUST_CALLBACK_INTERVAL_MS) {
1213             postAdjustCallbacks(MIN_ADJUST_CALLBACK_INTERVAL_MS);
1214             return;
1215         }
1216 
1217         mLastCallbackAdjustmentTimeElapsed = nowElapsed;
1218         mSortedStats.clear();
1219 
1220         for (int u = 0; u < mUidStats.size(); ++u) {
1221             UidStats us = mUidStats.valueAt(u);
1222             ArraySet<JobStatus> jobs = mTrackedJobs.get(us.uid);
1223             if (jobs == null || jobs.size() == 0) {
1224                 unregisterDefaultNetworkCallbackLocked(us.uid, nowElapsed);
1225                 continue;
1226             }
1227 
1228             // We won't evaluate stats in the first 30 seconds after boot...That's probably okay.
1229             if (us.lastUpdatedElapsed + MIN_STATS_UPDATE_INTERVAL_MS < nowElapsed) {
1230                 us.earliestEnqueueTime = Long.MAX_VALUE;
1231                 us.earliestEJEnqueueTime = Long.MAX_VALUE;
1232                 us.earliestUIJEnqueueTime = Long.MAX_VALUE;
1233                 us.numReadyWithConnectivity = 0;
1234                 us.numRequestedNetworkAvailable = 0;
1235                 us.numRegular = 0;
1236                 us.numEJs = 0;
1237                 us.numUIJs = 0;
1238 
1239                 for (int j = 0; j < jobs.size(); ++j) {
1240                     JobStatus job = jobs.valueAt(j);
1241                     if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_CONNECTIVITY)) {
1242                         us.numReadyWithConnectivity++;
1243                         if (isNetworkAvailable(job)) {
1244                             us.numRequestedNetworkAvailable++;
1245                         }
1246                         // Only use the enqueue time of jobs that would be ready to prevent apps
1247                         // from gaming the system (eg. by scheduling a job that requires all
1248                         // constraints and has a minimum latency of 6 months to always have the
1249                         // earliest enqueue time).
1250                         us.earliestEnqueueTime = Math.min(us.earliestEnqueueTime, job.enqueueTime);
1251                         if (job.shouldTreatAsExpeditedJob() || job.startedAsExpeditedJob) {
1252                             us.earliestEJEnqueueTime =
1253                                     Math.min(us.earliestEJEnqueueTime, job.enqueueTime);
1254                         } else if (job.shouldTreatAsUserInitiatedJob()) {
1255                             us.earliestUIJEnqueueTime =
1256                                     Math.min(us.earliestUIJEnqueueTime, job.enqueueTime);
1257                         }
1258                     }
1259                     if (job.shouldTreatAsExpeditedJob() || job.startedAsExpeditedJob) {
1260                         us.numEJs++;
1261                     } else if (job.shouldTreatAsUserInitiatedJob()) {
1262                         us.numUIJs++;
1263                     } else {
1264                         us.numRegular++;
1265                     }
1266                 }
1267 
1268                 us.lastUpdatedElapsed = nowElapsed;
1269             }
1270             mSortedStats.add(us);
1271         }
1272 
1273         mSortedStats.sort(mUidStatsComparator);
1274 
1275         final ArraySet<JobStatus> changedJobs = new ArraySet<>();
1276         // Iterate in reverse order to remove existing callbacks before adding new ones.
1277         for (int i = mSortedStats.size() - 1; i >= 0; --i) {
1278             UidStats us = mSortedStats.get(i);
1279             if (i >= MAX_NETWORK_CALLBACKS) {
1280                 if (unregisterDefaultNetworkCallbackLocked(us.uid, nowElapsed)) {
1281                     changedJobs.addAll(mTrackedJobs.get(us.uid));
1282                 }
1283             } else {
1284                 UidDefaultNetworkCallback defaultNetworkCallback =
1285                         mCurrentDefaultNetworkCallbacks.get(us.uid);
1286                 if (defaultNetworkCallback == null) {
1287                     // Not already registered.
1288                     defaultNetworkCallback = mDefaultNetworkCallbackPool.acquire();
1289                     if (defaultNetworkCallback == null) {
1290                         defaultNetworkCallback = new UidDefaultNetworkCallback();
1291                     }
1292                     defaultNetworkCallback.setUid(us.uid);
1293                     mCurrentDefaultNetworkCallbacks.append(us.uid, defaultNetworkCallback);
1294                     mConnManager.registerDefaultNetworkCallbackForUid(
1295                             us.uid, defaultNetworkCallback, mHandler);
1296                 }
1297             }
1298         }
1299         if (changedJobs.size() > 0) {
1300             mStateChangedListener.onControllerStateChanged(changedJobs);
1301         }
1302     }
1303 
1304     @GuardedBy("mLock")
unregisterDefaultNetworkCallbackLocked(int uid, long nowElapsed)1305     private boolean unregisterDefaultNetworkCallbackLocked(int uid, long nowElapsed) {
1306         UidDefaultNetworkCallback defaultNetworkCallback = mCurrentDefaultNetworkCallbacks.get(uid);
1307         if (defaultNetworkCallback == null) {
1308             return false;
1309         }
1310         mCurrentDefaultNetworkCallbacks.remove(uid);
1311         mConnManager.unregisterNetworkCallback(defaultNetworkCallback);
1312         mDefaultNetworkCallbackPool.release(defaultNetworkCallback);
1313         defaultNetworkCallback.clear();
1314 
1315         boolean changed = false;
1316         final ArraySet<JobStatus> jobs = mTrackedJobs.get(uid);
1317         if (jobs != null) {
1318             // Since we're unregistering the callback, we can no longer monitor
1319             // changes to the app's network and so we should just mark the
1320             // connectivity constraint as not satisfied.
1321             for (int j = jobs.size() - 1; j >= 0; --j) {
1322                 changed |= updateConstraintsSatisfied(
1323                         jobs.valueAt(j), nowElapsed, null, null);
1324             }
1325         }
1326         return changed;
1327     }
1328 
1329     @Nullable
getNetworkCapabilities(@ullable Network network)1330     public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
1331         final CachedNetworkMetadata metadata = getNetworkMetadata(network);
1332         return metadata == null ? null : metadata.networkCapabilities;
1333     }
1334 
1335     @Nullable
getNetworkMetadata(@ullable Network network)1336     private CachedNetworkMetadata getNetworkMetadata(@Nullable Network network) {
1337         if (network == null) {
1338             return null;
1339         }
1340         synchronized (mLock) {
1341             // There is technically a race here if the Network object is reused. This can happen
1342             // only if that Network disconnects and the auto-incrementing network ID in
1343             // ConnectivityService wraps. This shouldn't be a concern since we only make
1344             // use of asynchronous calls.
1345             return mAvailableNetworks.get(network);
1346         }
1347     }
1348 
1349     @GuardedBy("mLock")
1350     @Nullable
getNetworkLocked(@onNull JobStatus jobStatus)1351     private Network getNetworkLocked(@NonNull JobStatus jobStatus) {
1352         final UidDefaultNetworkCallback defaultNetworkCallback =
1353                 mCurrentDefaultNetworkCallbacks.get(jobStatus.getSourceUid());
1354         if (defaultNetworkCallback == null) {
1355             return null;
1356         }
1357 
1358         UidStats uidStats = mUidStats.get(jobStatus.getSourceUid());
1359 
1360         final int unbypassableBlockedReasons;
1361         // TOP will probably have fewer reasons, so we may not have to worry about returning
1362         // BG_BLOCKED for a TOP app. However, better safe than sorry.
1363         if (uidStats.baseBias >= JobInfo.BIAS_BOUND_FOREGROUND_SERVICE
1364                 || (jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1365             if (DEBUG) {
1366                 Slog.d(TAG, "Using FG bypass for " + jobStatus.getSourceUid());
1367             }
1368             unbypassableBlockedReasons = UNBYPASSABLE_FOREGROUND_BLOCKED_REASONS;
1369         } else if (jobStatus.shouldTreatAsUserInitiatedJob()) {
1370             if (DEBUG) {
1371                 Slog.d(TAG, "Using UI bypass for " + jobStatus.getSourceUid());
1372             }
1373             unbypassableBlockedReasons = UNBYPASSABLE_UI_BLOCKED_REASONS;
1374         } else if (jobStatus.shouldTreatAsExpeditedJob() || jobStatus.startedAsExpeditedJob) {
1375             if (DEBUG) {
1376                 Slog.d(TAG, "Using EJ bypass for " + jobStatus.getSourceUid());
1377             }
1378             unbypassableBlockedReasons = UNBYPASSABLE_EJ_BLOCKED_REASONS;
1379         } else {
1380             if (DEBUG) {
1381                 Slog.d(TAG, "Using BG bypass for " + jobStatus.getSourceUid());
1382             }
1383             unbypassableBlockedReasons = UNBYPASSABLE_BG_BLOCKED_REASONS;
1384         }
1385 
1386         return (unbypassableBlockedReasons & defaultNetworkCallback.mBlockedReasons) == 0
1387                 ? defaultNetworkCallback.mDefaultNetwork : null;
1388     }
1389 
updateConstraintsSatisfied(JobStatus jobStatus)1390     private boolean updateConstraintsSatisfied(JobStatus jobStatus) {
1391         final long nowElapsed = sElapsedRealtimeClock.millis();
1392         final UidDefaultNetworkCallback defaultNetworkCallback =
1393                 mCurrentDefaultNetworkCallbacks.get(jobStatus.getSourceUid());
1394         if (defaultNetworkCallback == null) {
1395             maybeRegisterDefaultNetworkCallbackLocked(jobStatus);
1396             return updateConstraintsSatisfied(jobStatus, nowElapsed, null, null);
1397         }
1398         final Network network = getNetworkLocked(jobStatus);
1399         final CachedNetworkMetadata networkMetadata = getNetworkMetadata(network);
1400         return updateConstraintsSatisfied(jobStatus, nowElapsed, network, networkMetadata);
1401     }
1402 
updateConstraintsSatisfied(JobStatus jobStatus, final long nowElapsed, Network network, @Nullable CachedNetworkMetadata networkMetadata)1403     private boolean updateConstraintsSatisfied(JobStatus jobStatus, final long nowElapsed,
1404             Network network, @Nullable CachedNetworkMetadata networkMetadata) {
1405         // TODO: consider matching against non-default networks
1406 
1407         final NetworkCapabilities capabilities =
1408                 networkMetadata == null ? null : networkMetadata.networkCapabilities;
1409         final boolean satisfied = isSatisfied(jobStatus, network, capabilities, mConstants);
1410 
1411         if (!satisfied && jobStatus.network != null
1412                 && mService.isCurrentlyRunningLocked(jobStatus)
1413                 && isSatisfied(jobStatus, jobStatus.network,
1414                         getNetworkCapabilities(jobStatus.network), mConstants)) {
1415             // A new network became available for a currently running job
1416             // (and most likely became the default network for the app),
1417             // but it doesn't yet satisfy the requested constraints and the old network
1418             // is still available and satisfies the constraints. Don't change the network
1419             // given to the job for now and let it keep running. We will re-evaluate when
1420             // the capabilities or connection state of either network change.
1421             if (DEBUG) {
1422                 Slog.i(TAG, "Not reassigning network from " + jobStatus.network
1423                         + " to " + network + " for running job " + jobStatus);
1424             }
1425             return false;
1426         }
1427 
1428         final boolean changed = jobStatus.setConnectivityConstraintSatisfied(nowElapsed, satisfied);
1429 
1430         jobStatus.setTransportAffinitiesSatisfied(satisfied && networkMetadata != null
1431                 && networkMetadata.satisfiesTransportAffinities);
1432         if (jobStatus.canApplyTransportAffinities()) {
1433             // Only modify the flex constraint if the job actually needs it.
1434             jobStatus.setFlexibilityConstraintSatisfied(nowElapsed,
1435                     mFlexibilityController.isFlexibilitySatisfiedLocked(jobStatus));
1436         }
1437 
1438         // Try to handle network transitions in a reasonable manner. See the lengthy note inside
1439         // UidDefaultNetworkCallback for more details.
1440         if (!changed && satisfied && jobStatus.network != null
1441                 && mService.isCurrentlyRunningLocked(jobStatus)) {
1442             // The job's connectivity constraint continues to be satisfied even though the network
1443             // has changed.
1444             // Inform the job of the new network so that it can attempt to switch over. This is the
1445             // ideal behavior for certain transitions such as going from a metered network to an
1446             // unmetered network.
1447             mStateChangedListener.onNetworkChanged(jobStatus, network);
1448         }
1449 
1450         // Pass along the evaluated network for job to use; prevents race
1451         // conditions as default routes change over time, and opens the door to
1452         // using non-default routes.
1453         jobStatus.network = network;
1454 
1455         if (DEBUG) {
1456             Slog.i(TAG, "Connectivity " + (changed ? "CHANGED" : "unchanged")
1457                     + " for " + jobStatus + ": usable=" + isUsable(capabilities)
1458                     + " satisfied=" + satisfied);
1459         }
1460         return changed;
1461     }
1462 
1463     @GuardedBy("mLock")
updateAllTrackedJobsLocked(boolean allowThrottle)1464     private void updateAllTrackedJobsLocked(boolean allowThrottle) {
1465         if (allowThrottle) {
1466             final long throttleTimeLeftMs =
1467                     (mLastAllJobUpdateTimeElapsed + mConstants.CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS)
1468                             - sElapsedRealtimeClock.millis();
1469             if (throttleTimeLeftMs > 0) {
1470                 Message msg = mHandler.obtainMessage(MSG_UPDATE_ALL_TRACKED_JOBS, 1, 0);
1471                 mHandler.sendMessageDelayed(msg, throttleTimeLeftMs);
1472                 return;
1473             }
1474         }
1475 
1476         mHandler.removeMessages(MSG_UPDATE_ALL_TRACKED_JOBS);
1477         updateTrackedJobsLocked(-1, null);
1478         mLastAllJobUpdateTimeElapsed = sElapsedRealtimeClock.millis();
1479     }
1480 
1481     /**
1482      * Update any jobs tracked by this controller that match given filters.
1483      *
1484      * @param filterUid     only update jobs belonging to this UID, or {@code -1} to
1485      *                      update all tracked jobs.
1486      * @param filterNetwork only update jobs that would use this
1487      *                      {@link Network}, or {@code null} to update all tracked jobs.
1488      */
1489     @GuardedBy("mLock")
updateTrackedJobsLocked(int filterUid, @Nullable Network filterNetwork)1490     private void updateTrackedJobsLocked(int filterUid, @Nullable Network filterNetwork) {
1491         final ArraySet<JobStatus> changedJobs;
1492         if (filterUid == -1) {
1493             changedJobs = new ArraySet<>();
1494             for (int i = mTrackedJobs.size() - 1; i >= 0; i--) {
1495                 if (updateTrackedJobsLocked(mTrackedJobs.valueAt(i), filterNetwork)) {
1496                     changedJobs.addAll(mTrackedJobs.valueAt(i));
1497                 }
1498             }
1499         } else {
1500             if (updateTrackedJobsLocked(mTrackedJobs.get(filterUid), filterNetwork)) {
1501                 changedJobs = mTrackedJobs.get(filterUid);
1502             } else {
1503                 changedJobs = null;
1504             }
1505         }
1506         if (changedJobs != null && changedJobs.size() > 0) {
1507             mStateChangedListener.onControllerStateChanged(changedJobs);
1508         }
1509     }
1510 
1511     @GuardedBy("mLock")
updateTrackedJobsLocked(ArraySet<JobStatus> jobs, @Nullable Network filterNetwork)1512     private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs,
1513             @Nullable Network filterNetwork) {
1514         if (jobs == null || jobs.size() == 0) {
1515             return false;
1516         }
1517 
1518         UidDefaultNetworkCallback defaultNetworkCallback =
1519                 mCurrentDefaultNetworkCallbacks.get(jobs.valueAt(0).getSourceUid());
1520         if (defaultNetworkCallback == null) {
1521             // This method is only called via a network callback object. That means something
1522             // changed about a general network characteristic (since we wouldn't be in this
1523             // situation if called from a UID_specific callback). The general network callback
1524             // will handle adjusting the per-UID callbacks, so nothing left to do here.
1525             return false;
1526         }
1527 
1528         final long nowElapsed = sElapsedRealtimeClock.millis();
1529         boolean changed = false;
1530         for (int i = jobs.size() - 1; i >= 0; i--) {
1531             final JobStatus js = jobs.valueAt(i);
1532 
1533             final Network net = getNetworkLocked(js);
1534             final boolean match = (filterNetwork == null
1535                     || Objects.equals(filterNetwork, net));
1536 
1537             // Update either when we have a network match, or when the
1538             // job hasn't yet been evaluated against the currently
1539             // active network; typically when we just lost a network.
1540             if (match || !Objects.equals(js.network, net)) {
1541                 changed |= updateConstraintsSatisfied(js, nowElapsed, net, getNetworkMetadata(net));
1542             }
1543         }
1544         return changed;
1545     }
1546 
1547     /**
1548      * Returns {@code true} if the job's assigned network is active or otherwise considered to be
1549      * in a good state to run the job now.
1550      */
1551     @GuardedBy("mLock")
isNetworkInStateForJobRunLocked(@onNull JobStatus jobStatus)1552     public boolean isNetworkInStateForJobRunLocked(@NonNull JobStatus jobStatus) {
1553         if (jobStatus.network == null) {
1554             return false;
1555         }
1556         if (jobStatus.shouldTreatAsExpeditedJob() || jobStatus.shouldTreatAsUserInitiatedJob()
1557                 || mService.getUidProcState(jobStatus.getSourceUid())
1558                         <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
1559             // EJs, UIJs, and BFGS+ jobs should be able to activate the network.
1560             return true;
1561         }
1562         return isNetworkInStateForJobRunLocked(jobStatus.network);
1563     }
1564 
1565     @GuardedBy("mLock")
1566     @VisibleForTesting
isNetworkInStateForJobRunLocked(@onNull Network network)1567     boolean isNetworkInStateForJobRunLocked(@NonNull Network network) {
1568         if (!Flags.batchConnectivityJobsPerNetwork()) {
1569             // Active network batching isn't enabled. We don't care about the network state.
1570             return true;
1571         }
1572 
1573         CachedNetworkMetadata cachedNetworkMetadata = mAvailableNetworks.get(network);
1574         if (cachedNetworkMetadata == null) {
1575             return false;
1576         }
1577 
1578         final long nowElapsed = sElapsedRealtimeClock.millis();
1579         if (cachedNetworkMetadata.defaultNetworkActivationLastConfirmedTimeElapsed
1580                 + mCcConfig.NETWORK_ACTIVATION_EXPIRATION_MS > nowElapsed) {
1581             // Network is still presumed to be active.
1582             return true;
1583         }
1584 
1585         final boolean inactiveForTooLong =
1586                 cachedNetworkMetadata.capabilitiesFirstAcquiredTimeElapsed
1587                         < nowElapsed - mCcConfig.NETWORK_ACTIVATION_MAX_WAIT_TIME_MS
1588                 && cachedNetworkMetadata.defaultNetworkActivationLastConfirmedTimeElapsed
1589                         < nowElapsed - mCcConfig.NETWORK_ACTIVATION_MAX_WAIT_TIME_MS;
1590         // We can only know the state of the system default network. If that's not available
1591         // or the network in question isn't the system default network,
1592         // then return true if we haven't gotten an active signal in a long time.
1593         if (mSystemDefaultNetwork == null) {
1594             return inactiveForTooLong;
1595         }
1596 
1597         if (!mSystemDefaultNetwork.equals(network)) {
1598             final NetworkCapabilities capabilities = cachedNetworkMetadata.networkCapabilities;
1599             if (capabilities != null
1600                     && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
1601                 // VPNs won't have an active signal sent for them. Check their underlying networks
1602                 // instead, prioritizing the system default if it's one of them.
1603                 final List<Network> underlyingNetworks = capabilities.getUnderlyingNetworks();
1604                 if (underlyingNetworks == null) {
1605                     return inactiveForTooLong;
1606                 }
1607 
1608                 if (underlyingNetworks.contains(mSystemDefaultNetwork)) {
1609                     if (DEBUG) {
1610                         Slog.i(TAG, "Substituting system default network "
1611                                 + mSystemDefaultNetwork + " for VPN " + network);
1612                     }
1613                     return isNetworkInStateForJobRunLocked(mSystemDefaultNetwork);
1614                 }
1615 
1616                 for (int i = underlyingNetworks.size() - 1; i >= 0; --i) {
1617                     if (isNetworkInStateForJobRunLocked(underlyingNetworks.get(i))) {
1618                         return true;
1619                     }
1620                 }
1621             }
1622             return inactiveForTooLong;
1623         }
1624 
1625         if (cachedNetworkMetadata.defaultNetworkActivationLastCheckTimeElapsed
1626                 + mCcConfig.NETWORK_ACTIVATION_EXPIRATION_MS < nowElapsed) {
1627             // We haven't checked the state recently enough. Let's check if the network is active.
1628             // However, if we checked after the last confirmed active time and it wasn't active,
1629             // then the network is still not active (we would be told when it becomes active
1630             // via onNetworkActive()).
1631             if (cachedNetworkMetadata.defaultNetworkActivationLastCheckTimeElapsed
1632                     > cachedNetworkMetadata.defaultNetworkActivationLastConfirmedTimeElapsed) {
1633                 return inactiveForTooLong;
1634             }
1635             // We need to explicitly check because there's no callback telling us when the network
1636             // leaves the high power state.
1637             cachedNetworkMetadata.defaultNetworkActivationLastCheckTimeElapsed = nowElapsed;
1638             final boolean isActive = mConnManager.isDefaultNetworkActive();
1639             if (isActive) {
1640                 cachedNetworkMetadata.defaultNetworkActivationLastConfirmedTimeElapsed = nowElapsed;
1641                 return true;
1642             }
1643             return inactiveForTooLong;
1644         }
1645 
1646         // We checked the state recently enough, but the network wasn't active. Assume it still
1647         // isn't active.
1648         return false;
1649     }
1650 
1651     /**
1652      * We know the network has just come up. We want to run any jobs that are ready.
1653      */
1654     @Override
1655     public void onNetworkActive() {
1656         synchronized (mLock) {
1657             if (mSystemDefaultNetwork == null) {
1658                 Slog.wtf(TAG, "System default network is unknown but active");
1659                 return;
1660             }
1661 
1662             CachedNetworkMetadata cachedNetworkMetadata =
1663                     mAvailableNetworks.get(mSystemDefaultNetwork);
1664             if (cachedNetworkMetadata == null) {
1665                 Slog.wtf(TAG, "System default network capabilities are unknown but active");
1666                 return;
1667             }
1668 
1669             // This method gets called on the system's main thread (not the
1670             // AppSchedulingModuleThread), so shift the processing work to a handler to avoid
1671             // blocking important operations on the main thread.
1672             cachedNetworkMetadata.defaultNetworkActivationLastConfirmedTimeElapsed =
1673                     cachedNetworkMetadata.defaultNetworkActivationLastCheckTimeElapsed =
1674                             sElapsedRealtimeClock.millis();
1675             mHandler.sendEmptyMessage(MSG_PROCESS_ACTIVE_NETWORK);
1676         }
1677     }
1678 
1679     /** NetworkCallback to track all network changes. */
1680     private final NetworkCallback mNetworkCallback = new NetworkCallback() {
1681         @Override
1682         public void onAvailable(Network network) {
1683             if (DEBUG) Slog.v(TAG, "onAvailable: " + network);
1684             // Documentation says not to call getNetworkCapabilities here but wait for
1685             // onCapabilitiesChanged instead.  onCapabilitiesChanged should be called immediately
1686             // after this, so no need to update mAvailableNetworks here.
1687         }
1688 
1689         @Override
1690         public void onCapabilitiesChanged(Network network, NetworkCapabilities capabilities) {
1691             if (DEBUG) {
1692                 Slog.v(TAG, "onCapabilitiesChanged: " + network);
1693             }
1694             synchronized (mLock) {
1695                 CachedNetworkMetadata cnm = mAvailableNetworks.get(network);
1696                 if (cnm == null) {
1697                     cnm = new CachedNetworkMetadata();
1698                     cnm.capabilitiesFirstAcquiredTimeElapsed = sElapsedRealtimeClock.millis();
1699                     mAvailableNetworks.put(network, cnm);
1700                 } else {
1701                     final NetworkCapabilities oldCaps = cnm.networkCapabilities;
1702                     if (oldCaps != null) {
1703                         maybeUnregisterSignalStrengthCallbackLocked(oldCaps);
1704                     }
1705                 }
1706                 cnm.networkCapabilities = capabilities;
1707                 if (updateTransportAffinitySatisfaction(cnm)) {
1708                     maybeUpdateFlexConstraintLocked(cnm);
1709                 }
1710                 maybeRegisterSignalStrengthCallbackLocked(capabilities);
1711                 updateTrackedJobsLocked(-1, network);
1712                 postAdjustCallbacks();
1713             }
1714         }
1715 
1716         @Override
1717         public void onLost(Network network) {
1718             if (DEBUG) {
1719                 Slog.v(TAG, "onLost: " + network);
1720             }
1721             synchronized (mLock) {
1722                 final CachedNetworkMetadata cnm = mAvailableNetworks.remove(network);
1723                 if (cnm != null) {
1724                     if (cnm.networkCapabilities != null) {
1725                         maybeUnregisterSignalStrengthCallbackLocked(cnm.networkCapabilities);
1726                     }
1727                     if (cnm.satisfiesTransportAffinities) {
1728                         maybeUpdateFlexConstraintLocked(null);
1729                     }
1730                 }
1731                 for (int u = 0; u < mCurrentDefaultNetworkCallbacks.size(); ++u) {
1732                     UidDefaultNetworkCallback callback = mCurrentDefaultNetworkCallbacks.valueAt(u);
1733                     if (Objects.equals(callback.mDefaultNetwork, network)) {
1734                         callback.mDefaultNetwork = null;
1735                     }
1736                 }
1737                 updateTrackedJobsLocked(-1, network);
1738                 postAdjustCallbacks();
1739             }
1740         }
1741 
1742         @GuardedBy("mLock")
1743         private void maybeRegisterSignalStrengthCallbackLocked(
1744                 @NonNull NetworkCapabilities capabilities) {
1745             if (!capabilities.hasTransport(TRANSPORT_CELLULAR)) {
1746                 return;
1747             }
1748             TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
1749             final Set<Integer> subscriptionIds = capabilities.getSubscriptionIds();
1750             for (int subId : subscriptionIds) {
1751                 if (mSignalStrengths.indexOfKey(subId) >= 0) {
1752                     continue;
1753                 }
1754                 TelephonyManager idTm = telephonyManager.createForSubscriptionId(subId);
1755                 CellSignalStrengthCallback callback = new CellSignalStrengthCallback();
1756                 idTm.registerTelephonyCallback(
1757                         AppSchedulingModuleThread.getExecutor(), callback);
1758                 mSignalStrengths.put(subId, callback);
1759 
1760                 final SignalStrength signalStrength = idTm.getSignalStrength();
1761                 if (signalStrength != null) {
1762                     callback.signalStrength = signalStrength.getLevel();
1763                 }
1764             }
1765         }
1766 
1767         @GuardedBy("mLock")
1768         private void maybeUnregisterSignalStrengthCallbackLocked(
1769                 @NonNull NetworkCapabilities capabilities) {
1770             if (!capabilities.hasTransport(TRANSPORT_CELLULAR)) {
1771                 return;
1772             }
1773             ArraySet<Integer> activeIds = new ArraySet<>();
1774             for (int i = 0, size = mAvailableNetworks.size(); i < size; ++i) {
1775                 final CachedNetworkMetadata metadata = mAvailableNetworks.valueAt(i);
1776                 if (metadata == null || metadata.networkCapabilities == null) {
1777                     continue;
1778                 }
1779                 if (metadata.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
1780                     activeIds.addAll(metadata.networkCapabilities.getSubscriptionIds());
1781                 }
1782             }
1783             if (DEBUG) {
1784                 Slog.d(TAG, "Active subscription IDs: " + activeIds);
1785             }
1786             TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
1787             Set<Integer> subscriptionIds = capabilities.getSubscriptionIds();
1788             for (int subId : subscriptionIds) {
1789                 if (activeIds.contains(subId)) {
1790                     continue;
1791                 }
1792                 TelephonyManager idTm = telephonyManager.createForSubscriptionId(subId);
1793                 CellSignalStrengthCallback callback = mSignalStrengths.removeReturnOld(subId);
1794                 if (callback != null) {
1795                     idTm.unregisterTelephonyCallback(callback);
1796                 } else {
1797                     Slog.wtf(TAG, "Callback for sub " + subId + " didn't exist?!?!");
1798                 }
1799             }
1800         }
1801 
1802         /**
1803          * Maybe call {@link FlexibilityController#setConstraintSatisfied(int, boolean, long)}
1804          * if the network affinity state has changed.
1805          */
1806         @GuardedBy("mLock")
1807         private void maybeUpdateFlexConstraintLocked(
1808                 @Nullable CachedNetworkMetadata cachedNetworkMetadata) {
1809             if (cachedNetworkMetadata != null
1810                     && cachedNetworkMetadata.satisfiesTransportAffinities) {
1811                 mFlexibilityController.setConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY,
1812                         true, sElapsedRealtimeClock.millis());
1813             } else {
1814                 // This network doesn't satisfy transport affinities. Check if any other
1815                 // available networks do satisfy the affinities before saying that the
1816                 // transport affinity is no longer satisfied for flex.
1817                 boolean isTransportAffinitySatisfied = false;
1818                 for (int i = mAvailableNetworks.size() - 1; i >= 0; --i) {
1819                     final CachedNetworkMetadata cnm = mAvailableNetworks.valueAt(i);
1820                     if (cnm != null && cnm.satisfiesTransportAffinities) {
1821                         isTransportAffinitySatisfied = true;
1822                         break;
1823                     }
1824                 }
1825                 if (!isTransportAffinitySatisfied) {
1826                     mFlexibilityController.setConstraintSatisfied(
1827                             JobStatus.CONSTRAINT_CONNECTIVITY, false,
1828                             sElapsedRealtimeClock.millis());
1829                 }
1830             }
1831         }
1832     };
1833 
1834     /** NetworkCallback to track only changes to the default network. */
1835     private final NetworkCallback mDefaultNetworkCallback = new NetworkCallback() {
1836         @Override
1837         public void onAvailable(Network network) {
1838             if (DEBUG) Slog.v(TAG, "systemDefault-onAvailable: " + network);
1839             synchronized (mLock) {
1840                 mSystemDefaultNetwork = network;
1841             }
1842         }
1843 
1844         @Override
1845         public void onLost(Network network) {
1846             if (DEBUG) {
1847                 Slog.v(TAG, "systemDefault-onLost: " + network);
1848             }
1849             synchronized (mLock) {
1850                 if (network.equals(mSystemDefaultNetwork)) {
1851                     mSystemDefaultNetwork = null;
1852                 }
1853             }
1854         }
1855     };
1856 
1857     private final INetworkPolicyListener mNetPolicyListener = new NetworkPolicyManager.Listener() {
1858         @Override
1859         public void onRestrictBackgroundChanged(boolean restrictBackground) {
1860             if (DEBUG) {
1861                 Slog.v(TAG, "onRestrictBackgroundChanged: " + restrictBackground);
1862             }
1863             mHandler.obtainMessage(MSG_DATA_SAVER_TOGGLED).sendToTarget();
1864         }
1865 
1866         @Override
1867         public void onUidPoliciesChanged(int uid, int uidPolicies) {
1868             if (DEBUG) {
1869                 Slog.v(TAG, "onUidPoliciesChanged: " + uid);
1870             }
1871             mHandler.obtainMessage(MSG_UID_POLICIES_CHANGED,
1872                     uid, mNetPolicyManager.getRestrictBackgroundStatus(uid))
1873                     .sendToTarget();
1874         }
1875     };
1876 
1877     private class CcHandler extends Handler {
1878         CcHandler(Looper looper) {
1879             super(looper);
1880         }
1881 
1882         @Override
1883         public void handleMessage(Message msg) {
1884             synchronized (mLock) {
1885                 switch (msg.what) {
1886                     case MSG_ADJUST_CALLBACKS:
1887                         synchronized (mLock) {
1888                             maybeAdjustRegisteredCallbacksLocked();
1889                         }
1890                         break;
1891 
1892                     case MSG_UPDATE_ALL_TRACKED_JOBS:
1893                         synchronized (mLock) {
1894                             final boolean allowThrottle = msg.arg1 == 1;
1895                             updateAllTrackedJobsLocked(allowThrottle);
1896                         }
1897                         break;
1898 
1899                     case MSG_DATA_SAVER_TOGGLED:
1900                         removeMessages(MSG_DATA_SAVER_TOGGLED);
1901                         synchronized (mLock) {
1902                             mBackgroundMeteredAllowed.clear();
1903                             updateTrackedJobsLocked(-1, null);
1904                         }
1905                         break;
1906 
1907                     case MSG_UID_POLICIES_CHANGED:
1908                         final int uid = msg.arg1;
1909                         final boolean newAllowed =
1910                                 msg.arg2 != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
1911                         synchronized (mLock) {
1912                             final boolean oldAllowed = mBackgroundMeteredAllowed.get(uid);
1913                             if (oldAllowed != newAllowed) {
1914                                 mBackgroundMeteredAllowed.put(uid, newAllowed);
1915                                 updateTrackedJobsLocked(uid, null);
1916                             }
1917                         }
1918                         break;
1919 
1920                     case MSG_PROCESS_ACTIVE_NETWORK:
1921                         removeMessages(MSG_PROCESS_ACTIVE_NETWORK);
1922                         synchronized (mLock) {
1923                             if (mSystemDefaultNetwork == null) {
1924                                 break;
1925                             }
1926                             if (!Flags.batchConnectivityJobsPerNetwork()) {
1927                                 break;
1928                             }
1929                             if (!isNetworkInStateForJobRunLocked(mSystemDefaultNetwork)) {
1930                                 break;
1931                             }
1932 
1933                             final ArrayMap<Network, Boolean> includeInProcessing = new ArrayMap<>();
1934                             // Try to get the jobs to piggyback on the active network.
1935                             for (int u = mTrackedJobs.size() - 1; u >= 0; --u) {
1936                                 final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(u);
1937                                 for (int j = jobs.size() - 1; j >= 0; --j) {
1938                                     final JobStatus js = jobs.valueAt(j);
1939                                     if (!mSystemDefaultNetwork.equals(js.network)) {
1940                                         final NetworkCapabilities capabilities =
1941                                                 getNetworkCapabilities(js.network);
1942                                         if (capabilities == null
1943                                                 || !capabilities.hasTransport(
1944                                                 NetworkCapabilities.TRANSPORT_VPN)) {
1945                                             includeInProcessing.put(js.network, Boolean.FALSE);
1946                                             continue;
1947                                         }
1948                                         if (includeInProcessing.containsKey(js.network)) {
1949                                             if (!includeInProcessing.get(js.network)) {
1950                                                 continue;
1951                                             }
1952                                         } else {
1953                                             // VPNs most likely use the system default network as
1954                                             // their underlying network. If so, process the job.
1955                                             final List<Network> underlyingNetworks =
1956                                                     capabilities.getUnderlyingNetworks();
1957                                             final boolean isSystemDefaultInUnderlying =
1958                                                     underlyingNetworks != null
1959                                                             && underlyingNetworks.contains(
1960                                                                     mSystemDefaultNetwork);
1961                                             includeInProcessing.put(js.network,
1962                                                     isSystemDefaultInUnderlying);
1963                                             if (!isSystemDefaultInUnderlying) {
1964                                                 continue;
1965                                             }
1966                                         }
1967                                     }
1968                                     if (js.isReady()) {
1969                                         if (DEBUG) {
1970                                             Slog.d(TAG, "Potentially running " + js
1971                                                     + " due to network activity");
1972                                         }
1973                                         mStateChangedListener.onRunJobNow(js);
1974                                     }
1975                                 }
1976                             }
1977                         }
1978                         break;
1979                 }
1980             }
1981         }
1982     }
1983 
1984     @VisibleForTesting
1985     class CcConfig {
1986         private boolean mFlexIsEnabled =
1987                 FlexibilityController.FcConfig.DEFAULT_APPLIED_CONSTRAINTS != 0;
1988         private boolean mShouldReprocessNetworkCapabilities = false;
1989 
1990         /**
1991          * Prefix to use with all constant keys in order to "sub-namespace" the keys.
1992          * "conn_" is used for legacy reasons.
1993          */
1994         private static final String CC_CONFIG_PREFIX = "conn_";
1995 
1996         @VisibleForTesting
1997         static final String KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY =
1998                 CC_CONFIG_PREFIX + "avoid_undefined_transport_affinity";
1999         private static final String KEY_NETWORK_ACTIVATION_EXPIRATION_MS =
2000                 CC_CONFIG_PREFIX + "network_activation_expiration_ms";
2001         private static final String KEY_NETWORK_ACTIVATION_MAX_WAIT_TIME_MS =
2002                 CC_CONFIG_PREFIX + "network_activation_max_wait_time_ms";
2003 
2004         private static final boolean DEFAULT_AVOID_UNDEFINED_TRANSPORT_AFFINITY = false;
2005         private static final long DEFAULT_NETWORK_ACTIVATION_EXPIRATION_MS = 10000L;
2006         private static final long DEFAULT_NETWORK_ACTIVATION_MAX_WAIT_TIME_MS =
2007                 31 * MINUTE_IN_MILLIS;
2008 
2009         /**
2010          * If true, will avoid network transports that don't have an explicitly defined affinity.
2011          */
2012         public boolean AVOID_UNDEFINED_TRANSPORT_AFFINITY =
2013                 DEFAULT_AVOID_UNDEFINED_TRANSPORT_AFFINITY;
2014 
2015         /**
2016          * Amount of time that needs to pass before needing to determine if the network is still
2017          * active.
2018          */
2019         public long NETWORK_ACTIVATION_EXPIRATION_MS = DEFAULT_NETWORK_ACTIVATION_EXPIRATION_MS;
2020 
2021         /**
2022          * Max time to wait since the network was last activated before deciding to allow jobs to
2023          * run even if the network isn't active
2024          */
2025         public long NETWORK_ACTIVATION_MAX_WAIT_TIME_MS =
2026                 DEFAULT_NETWORK_ACTIVATION_MAX_WAIT_TIME_MS;
2027 
2028         @GuardedBy("mLock")
2029         public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
2030                 @NonNull String key) {
2031             switch (key) {
2032                 case KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY:
2033                     final boolean avoid = properties.getBoolean(key,
2034                             DEFAULT_AVOID_UNDEFINED_TRANSPORT_AFFINITY);
2035                     if (AVOID_UNDEFINED_TRANSPORT_AFFINITY != avoid) {
2036                         AVOID_UNDEFINED_TRANSPORT_AFFINITY = avoid;
2037                         mShouldReprocessNetworkCapabilities = true;
2038                     }
2039                     break;
2040                 case KEY_NETWORK_ACTIVATION_EXPIRATION_MS:
2041                     final long gracePeriodMs = properties.getLong(key,
2042                             DEFAULT_NETWORK_ACTIVATION_EXPIRATION_MS);
2043                     if (NETWORK_ACTIVATION_EXPIRATION_MS != gracePeriodMs) {
2044                         NETWORK_ACTIVATION_EXPIRATION_MS = gracePeriodMs;
2045                         // This doesn't need to trigger network capability reprocessing.
2046                     }
2047                     break;
2048                 case KEY_NETWORK_ACTIVATION_MAX_WAIT_TIME_MS:
2049                     final long maxWaitMs = properties.getLong(key,
2050                             DEFAULT_NETWORK_ACTIVATION_MAX_WAIT_TIME_MS);
2051                     if (NETWORK_ACTIVATION_MAX_WAIT_TIME_MS != maxWaitMs) {
2052                         NETWORK_ACTIVATION_MAX_WAIT_TIME_MS = maxWaitMs;
2053                         mShouldReprocessNetworkCapabilities = true;
2054                     }
2055                     break;
2056             }
2057         }
2058 
2059         private void dump(IndentingPrintWriter pw) {
2060             pw.println();
2061             pw.print(ConnectivityController.class.getSimpleName());
2062             pw.println(":");
2063             pw.increaseIndent();
2064 
2065             pw.print(KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY,
2066                     AVOID_UNDEFINED_TRANSPORT_AFFINITY).println();
2067             pw.print(KEY_NETWORK_ACTIVATION_EXPIRATION_MS,
2068                     NETWORK_ACTIVATION_EXPIRATION_MS).println();
2069             pw.print(KEY_NETWORK_ACTIVATION_MAX_WAIT_TIME_MS,
2070                     NETWORK_ACTIVATION_MAX_WAIT_TIME_MS).println();
2071 
2072             pw.decreaseIndent();
2073         }
2074     }
2075 
2076     private class UidDefaultNetworkCallback extends NetworkCallback {
2077         private int mUid;
2078         @Nullable
2079         private Network mDefaultNetwork;
2080         private int mBlockedReasons;
2081 
2082         private void setUid(int uid) {
2083             mUid = uid;
2084             mDefaultNetwork = null;
2085         }
2086 
2087         private void clear() {
2088             mDefaultNetwork = null;
2089             mUid = UserHandle.USER_NULL;
2090         }
2091 
2092         @Override
2093         public void onAvailable(Network network) {
2094             if (DEBUG) Slog.v(TAG, "default-onAvailable(" + mUid + "): " + network);
2095         }
2096 
2097         @Override
2098         public void onBlockedStatusChanged(Network network, int blockedReasons) {
2099             if (DEBUG) {
2100                 Slog.v(TAG, "default-onBlockedStatusChanged(" + mUid + "): "
2101                         + network + " -> " + blockedReasons);
2102             }
2103             if (mUid == UserHandle.USER_NULL) {
2104                 return;
2105             }
2106             synchronized (mLock) {
2107                 mDefaultNetwork = network;
2108                 mBlockedReasons = blockedReasons;
2109                 updateTrackedJobsLocked(mUid, network);
2110             }
2111         }
2112 
2113         // Network transitions have some complicated behavior that JS doesn't handle very well.
2114         //
2115         // * If the default network changes from A to B without A disconnecting, then we'll only
2116         // get onAvailable(B) (and the subsequent onBlockedStatusChanged() call). Since we get
2117         // the onBlockedStatusChanged() call, we re-evaluate the job, but keep it running
2118         // (assuming the new network satisfies constraints). The app continues to use the old
2119         // network (if they use the network object provided through JobParameters.getNetwork())
2120         // because we don't notify them of the default network change. If the old network later
2121         // stops satisfying requested constraints, then we have a problem. Depending on the order
2122         // of calls, if the per-UID callback gets notified of the network change before the
2123         // general callback gets notified of the capabilities change, then the job's network
2124         // object will point to the new network and we won't stop the job, even though we told it
2125         // to use the old network that no longer satisfies its constraints. This is the behavior
2126         // we loosely had (ignoring race conditions between asynchronous and synchronous
2127         // connectivity calls) when we were calling the synchronous getActiveNetworkForUid() API.
2128         // However, we should fix it.
2129         // TODO: stop jobs when the existing capabilities change after default network change
2130         //
2131         // * If the default network changes from A to B because A disconnected, then we'll get
2132         // onLost(A) and then onAvailable(B). In this case, there will be a short period where JS
2133         // doesn't think there's an available network for the job, so we'll stop the job even
2134         // though onAvailable(B) will be called soon. One on hand, the app would have gotten a
2135         // network error as well because of A's disconnect, and this will allow JS to provide the
2136         // job with the new default network. On the other hand, we have to stop the job even
2137         // though it could have continued running with the new network and the job has to deal
2138         // with whatever backoff policy is set. For now, the current behavior is fine, but we may
2139         // want to see if there's a way to have a smoother transition.
2140 
2141         @Override
2142         public void onLost(Network network) {
2143             if (DEBUG) {
2144                 Slog.v(TAG, "default-onLost(" + mUid + "): " + network);
2145             }
2146             if (mUid == UserHandle.USER_NULL) {
2147                 return;
2148             }
2149             synchronized (mLock) {
2150                 if (Objects.equals(mDefaultNetwork, network)) {
2151                     mDefaultNetwork = null;
2152                     updateTrackedJobsLocked(mUid, network);
2153                     // Add a delay in case onAvailable()+onBlockedStatusChanged is called for a
2154                     // new network. If this onLost was called because the network is completely
2155                     // gone, the delay will hel make sure we don't have a short burst of adjusting
2156                     // callback calls.
2157                     postAdjustCallbacks(1000);
2158                 }
2159             }
2160         }
2161 
2162         private void dumpLocked(IndentingPrintWriter pw) {
2163             pw.print("UID: ");
2164             pw.print(mUid);
2165             pw.print("; ");
2166             if (mDefaultNetwork == null) {
2167                 pw.print("No network");
2168             } else {
2169                 pw.print("Network: ");
2170                 pw.print(mDefaultNetwork);
2171                 pw.print(" (blocked=");
2172                 pw.print(NetworkPolicyManager.blockedReasonsToString(mBlockedReasons));
2173                 pw.print(")");
2174             }
2175             pw.println();
2176         }
2177     }
2178 
2179     private static class CachedNetworkMetadata {
2180         public NetworkCapabilities networkCapabilities;
2181         public boolean satisfiesTransportAffinities;
2182         /**
2183          * Track the first time ConnectivityController was informed about the capabilities of the
2184          * network after it became available.
2185          */
2186         public long capabilitiesFirstAcquiredTimeElapsed;
2187         public long defaultNetworkActivationLastCheckTimeElapsed;
2188         public long defaultNetworkActivationLastConfirmedTimeElapsed;
2189 
2190         public String toString() {
2191             return "CNM{"
2192                     + networkCapabilities.toString()
2193                     + ", satisfiesTransportAffinities=" + satisfiesTransportAffinities
2194                     + ", capabilitiesFirstAcquiredTimeElapsed="
2195                             + capabilitiesFirstAcquiredTimeElapsed
2196                     + ", defaultNetworkActivationLastCheckTimeElapsed="
2197                             + defaultNetworkActivationLastCheckTimeElapsed
2198                     + ", defaultNetworkActivationLastConfirmedTimeElapsed="
2199                             + defaultNetworkActivationLastConfirmedTimeElapsed
2200                     + "}";
2201         }
2202     }
2203 
2204     private static class UidStats {
2205         public final int uid;
2206         public int baseBias;
2207         public final ArraySet<JobStatus> runningJobs = new ArraySet<>();
2208         public int numReadyWithConnectivity;
2209         public int numRequestedNetworkAvailable;
2210         public int numEJs;
2211         public int numRegular;
2212         public int numUIJs;
2213         public long earliestEnqueueTime;
2214         public long earliestEJEnqueueTime;
2215         public long earliestUIJEnqueueTime;
2216         public long lastUpdatedElapsed;
2217 
2218         private UidStats(int uid) {
2219             this.uid = uid;
2220         }
2221 
2222         private void dumpLocked(IndentingPrintWriter pw, final long nowElapsed) {
2223             pw.print("UidStats{");
2224             pw.print("uid", uid);
2225             pw.print("pri", baseBias);
2226             pw.print("#run", runningJobs.size());
2227             pw.print("#readyWithConn", numReadyWithConnectivity);
2228             pw.print("#netAvail", numRequestedNetworkAvailable);
2229             pw.print("#EJs", numEJs);
2230             pw.print("#reg", numRegular);
2231             pw.print("earliestEnqueue", earliestEnqueueTime);
2232             pw.print("earliestEJEnqueue", earliestEJEnqueueTime);
2233             pw.print("earliestUIJEnqueue", earliestUIJEnqueueTime);
2234             pw.print("updated=");
2235             TimeUtils.formatDuration(lastUpdatedElapsed - nowElapsed, pw);
2236             pw.println("}");
2237         }
2238     }
2239 
2240     private class CellSignalStrengthCallback extends TelephonyCallback
2241             implements TelephonyCallback.SignalStrengthsListener {
2242         @GuardedBy("mLock")
2243         public int signalStrength = CellSignalStrength.SIGNAL_STRENGTH_GREAT;
2244 
2245         @Override
2246         public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) {
2247             synchronized (mLock) {
2248                 final int newSignalStrength = signalStrength.getLevel();
2249                 if (DEBUG) {
2250                     Slog.d(TAG, "Signal strength changing from "
2251                             + this.signalStrength + " to " + newSignalStrength);
2252                     for (CellSignalStrength css : signalStrength.getCellSignalStrengths()) {
2253                         Slog.d(TAG, "CSS: " + css.getLevel() + " " + css);
2254                     }
2255                 }
2256                 if (this.signalStrength == newSignalStrength) {
2257                     // This happens a lot.
2258                     return;
2259                 }
2260                 this.signalStrength = newSignalStrength;
2261                 // Update job bookkeeping out of band to avoid blocking callback progress.
2262                 mHandler.obtainMessage(MSG_UPDATE_ALL_TRACKED_JOBS, 1, 0).sendToTarget();
2263             }
2264         }
2265     }
2266 
2267     @VisibleForTesting
2268     @NonNull
2269     CcConfig getCcConfig() {
2270         return mCcConfig;
2271     }
2272 
2273     @Override
2274     public void dumpConstants(IndentingPrintWriter pw) {
2275         mCcConfig.dump(pw);
2276     }
2277 
2278     @GuardedBy("mLock")
2279     @Override
2280     public void dumpControllerStateLocked(IndentingPrintWriter pw,
2281             Predicate<JobStatus> predicate) {
2282         final long nowElapsed = sElapsedRealtimeClock.millis();
2283 
2284         pw.println("Aconfig flags:");
2285         pw.increaseIndent();
2286         pw.print(FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER,
2287                 Flags.relaxPrefetchConnectivityConstraintOnlyOnCharger());
2288         pw.println();
2289         pw.decreaseIndent();
2290         pw.println();
2291 
2292         if (mRequestedWhitelistJobs.size() > 0) {
2293             pw.print("Requested standby exceptions:");
2294             for (int i = 0; i < mRequestedWhitelistJobs.size(); i++) {
2295                 pw.print(" ");
2296                 pw.print(mRequestedWhitelistJobs.keyAt(i));
2297                 pw.print(" (");
2298                 pw.print(mRequestedWhitelistJobs.valueAt(i).size());
2299                 pw.print(" jobs)");
2300             }
2301             pw.println();
2302         }
2303         if (mAvailableNetworks.size() > 0) {
2304             pw.println("Available networks:");
2305             pw.increaseIndent();
2306             for (int i = 0; i < mAvailableNetworks.size(); i++) {
2307                 pw.print(mAvailableNetworks.keyAt(i));
2308                 pw.print(": ");
2309                 pw.println(mAvailableNetworks.valueAt(i));
2310             }
2311             pw.decreaseIndent();
2312         } else {
2313             pw.println("No available networks");
2314         }
2315         pw.println();
2316 
2317         if (mSignalStrengths.size() > 0) {
2318             pw.println("Subscription ID signal strengths:");
2319             pw.increaseIndent();
2320             for (int i = 0; i < mSignalStrengths.size(); ++i) {
2321                 pw.print(mSignalStrengths.keyAt(i));
2322                 pw.print(": ");
2323                 pw.println(mSignalStrengths.valueAt(i).signalStrength);
2324             }
2325             pw.decreaseIndent();
2326         } else {
2327             pw.println("No cached signal strengths");
2328         }
2329         pw.println();
2330 
2331         if (mBackgroundMeteredAllowed.size() > 0) {
2332             pw.print("Background metered allowed: ");
2333             pw.println(mBackgroundMeteredAllowed);
2334             pw.println();
2335         }
2336 
2337         pw.println("Current default network callbacks:");
2338         pw.increaseIndent();
2339         for (int i = 0; i < mCurrentDefaultNetworkCallbacks.size(); i++) {
2340             mCurrentDefaultNetworkCallbacks.valueAt(i).dumpLocked(pw);
2341         }
2342         pw.decreaseIndent();
2343         pw.println();
2344 
2345         pw.println("UID Pecking Order:");
2346         pw.increaseIndent();
2347         for (int i = 0; i < mSortedStats.size(); ++i) {
2348             pw.print(i);
2349             pw.print(": ");
2350             mSortedStats.get(i).dumpLocked(pw, nowElapsed);
2351         }
2352         pw.decreaseIndent();
2353         pw.println();
2354 
2355         for (int i = 0; i < mTrackedJobs.size(); i++) {
2356             final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i);
2357             for (int j = 0; j < jobs.size(); j++) {
2358                 final JobStatus js = jobs.valueAt(j);
2359                 if (!predicate.test(js)) {
2360                     continue;
2361                 }
2362                 pw.print("#");
2363                 js.printUniqueId(pw);
2364                 pw.print(" from ");
2365                 UserHandle.formatUid(pw, js.getSourceUid());
2366                 pw.print(": ");
2367                 pw.print(js.getJob().getRequiredNetwork());
2368                 pw.println();
2369             }
2370         }
2371     }
2372 
2373     @GuardedBy("mLock")
2374     @Override
2375     public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
2376             Predicate<JobStatus> predicate) {
2377         final long token = proto.start(fieldId);
2378         final long mToken = proto.start(StateControllerProto.CONNECTIVITY);
2379 
2380         for (int i = 0; i < mRequestedWhitelistJobs.size(); i++) {
2381             proto.write(
2382                     StateControllerProto.ConnectivityController.REQUESTED_STANDBY_EXCEPTION_UIDS,
2383                     mRequestedWhitelistJobs.keyAt(i));
2384         }
2385         for (int i = 0; i < mTrackedJobs.size(); i++) {
2386             final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i);
2387             for (int j = 0; j < jobs.size(); j++) {
2388                 final JobStatus js = jobs.valueAt(j);
2389                 if (!predicate.test(js)) {
2390                     continue;
2391                 }
2392                 final long jsToken = proto.start(
2393                         StateControllerProto.ConnectivityController.TRACKED_JOBS);
2394                 js.writeToShortProto(proto,
2395                         StateControllerProto.ConnectivityController.TrackedJob.INFO);
2396                 proto.write(StateControllerProto.ConnectivityController.TrackedJob.SOURCE_UID,
2397                         js.getSourceUid());
2398                 proto.end(jsToken);
2399             }
2400         }
2401 
2402         proto.end(mToken);
2403         proto.end(token);
2404     }
2405 }
2406