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