Loading apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +46 −0 Original line number Diff line number Diff line Loading @@ -417,6 +417,9 @@ public class JobSchedulerService extends com.android.server.SystemService break; case Constants.KEY_CONN_CONGESTION_DELAY_FRAC: case Constants.KEY_CONN_PREFETCH_RELAX_FRAC: case Constants.KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC: case Constants.KEY_CONN_USE_CELL_SIGNAL_STRENGTH: case Constants.KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS: mConstants.updateConnectivityConstantsLocked(); break; case Constants.KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS: Loading Loading @@ -489,6 +492,12 @@ public class JobSchedulerService extends com.android.server.SystemService private static final String KEY_MIN_EXP_BACKOFF_TIME_MS = "min_exp_backoff_time_ms"; private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac"; private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac"; private static final String KEY_CONN_USE_CELL_SIGNAL_STRENGTH = "conn_use_cell_signal_strength"; private static final String KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS = "conn_update_all_jobs_min_interval_ms"; private static final String KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC = "conn_low_signal_strength_relax_frac"; private static final String KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS = "prefetch_force_batch_relax_threshold_ms"; private static final String KEY_ENABLE_API_QUOTAS = "enable_api_quotas"; Loading @@ -514,6 +523,9 @@ public class JobSchedulerService extends com.android.server.SystemService private static final long DEFAULT_MIN_EXP_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS; private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f; private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f; private static final boolean DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH = true; private static final long DEFAULT_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS = MINUTE_IN_MILLIS; private static final float DEFAULT_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC = 0.5f; private static final long DEFAULT_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS = HOUR_IN_MILLIS; private static final boolean DEFAULT_ENABLE_API_QUOTAS = true; private static final int DEFAULT_API_QUOTA_SCHEDULE_COUNT = 250; Loading Loading @@ -569,6 +581,23 @@ public class JobSchedulerService extends com.android.server.SystemService * we consider matching it against a metered network. */ public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC; /** * Whether to use the cell signal strength to determine if a particular job is eligible to * run. */ public boolean CONN_USE_CELL_SIGNAL_STRENGTH = DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH; /** * When throttling updating all tracked jobs, make sure not to update them more frequently * than this value. */ public long CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS = DEFAULT_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS; /** * The fraction of a job's running window that must pass before we consider running it on * low signal strength networks. */ public float CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC = DEFAULT_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC; /** * The amount of time within which we would consider the app to be launching relatively soon Loading Loading @@ -661,6 +690,18 @@ public class JobSchedulerService extends com.android.server.SystemService CONN_PREFETCH_RELAX_FRAC = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_CONN_PREFETCH_RELAX_FRAC, DEFAULT_CONN_PREFETCH_RELAX_FRAC); CONN_USE_CELL_SIGNAL_STRENGTH = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_CONN_USE_CELL_SIGNAL_STRENGTH, DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH); CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS = DeviceConfig.getLong( DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS, DEFAULT_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS); CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC = DeviceConfig.getFloat( DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC, DEFAULT_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC); } private void updatePrefetchConstantsLocked() { Loading Loading @@ -739,6 +780,11 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println(); pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); pw.print(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println(); pw.print(KEY_CONN_USE_CELL_SIGNAL_STRENGTH, CONN_USE_CELL_SIGNAL_STRENGTH).println(); pw.print(KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS, CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS) .println(); pw.print(KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC, CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC) .println(); pw.print(KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS, PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS).println(); Loading apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +225 −9 Original line number Diff line number Diff line Loading @@ -35,6 +35,10 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.UserHandle; import android.telephony.CellSignalStrength; import android.telephony.SignalStrength; import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; Loading @@ -60,6 +64,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.Predicate; /** Loading Loading @@ -213,9 +218,16 @@ public final class ConnectivityController extends RestrictingController implemen * is only done in {@link #maybeAdjustRegisteredCallbacksLocked()} and may sometimes be stale. */ private final List<UidStats> mSortedStats = new ArrayList<>(); @GuardedBy("mLock") private long mLastCallbackAdjustmentTimeElapsed; @GuardedBy("mLock") private final SparseArray<CellSignalStrengthCallback> mSignalStrengths = new SparseArray<>(); @GuardedBy("mLock") private long mLastAllJobUpdateTimeElapsed; private static final int MSG_ADJUST_CALLBACKS = 0; private static final int MSG_UPDATE_ALL_TRACKED_JOBS = 1; private final Handler mHandler; Loading Loading @@ -529,11 +541,7 @@ public final class ConnectivityController extends RestrictingController implemen @GuardedBy("mLock") public void onBatteryStateChangedLocked() { // Update job bookkeeping out of band to avoid blocking broadcast progress. JobSchedulerBackgroundThread.getHandler().post(() -> { synchronized (mLock) { updateTrackedJobsLocked(-1, null); } }); mHandler.sendEmptyMessage(MSG_UPDATE_ALL_TRACKED_JOBS); } private boolean isUsable(NetworkCapabilities capabilities) { Loading Loading @@ -650,6 +658,82 @@ public final class ConnectivityController extends RestrictingController implemen } } @GuardedBy("mLock") private boolean isStrongEnough(JobStatus jobStatus, NetworkCapabilities capabilities, Constants constants) { final int priority = jobStatus.getEffectivePriority(); if (priority >= JobInfo.PRIORITY_HIGH) { return true; } if (!constants.CONN_USE_CELL_SIGNAL_STRENGTH) { return true; } if (!capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { return true; } if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { // Exclude VPNs because it's currently not possible to determine the VPN's underlying // network, and thus the correct signal strength of the VPN's network. // Transmitting data over a VPN is generally more battery-expensive than on the // underlying network, so: // TODO: find a good way to reduce job use of VPN when it'll be very expensive // For now, we just pretend VPNs are always strong enough return true; } // VCNs running over WiFi will declare TRANSPORT_CELLULAR. When connected, a VCN will // most likely be the default network. We ideally don't want this to restrict jobs when the // VCN incorrectly declares the CELLULAR transport, but there's currently no way to // determine if a network is a VCN. When there is: // TODO(216127782): exclude VCN running over WiFi from this check int signalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; // Use the best strength found. final Set<Integer> subscriptionIds = capabilities.getSubscriptionIds(); for (int subId : subscriptionIds) { CellSignalStrengthCallback callback = mSignalStrengths.get(subId); if (callback != null) { signalStrength = Math.max(signalStrength, callback.signalStrength); } else { Slog.wtf(TAG, "Subscription ID " + subId + " doesn't have a registered callback"); } } if (DEBUG) { Slog.d(TAG, "Cell signal strength for job=" + signalStrength); } // Treat "NONE_OR_UNKNOWN" as "NONE". if (signalStrength <= CellSignalStrength.SIGNAL_STRENGTH_POOR) { // If signal strength is poor, don't run MIN or LOW priority jobs, and only // run DEFAULT priority jobs if the device is charging or the job has been waiting // long enough. if (priority > JobInfo.PRIORITY_DEFAULT) { return true; } if (priority < JobInfo.PRIORITY_DEFAULT) { return false; } // DEFAULT job. return (mService.isBatteryCharging() && mService.isBatteryNotLow()) || jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC; } if (signalStrength <= CellSignalStrength.SIGNAL_STRENGTH_MODERATE) { // If signal strength is moderate, only run MIN priority jobs when the device // is charging, or the job is already running. if (priority >= JobInfo.PRIORITY_LOW) { return true; } // MIN job. if (mService.isBatteryCharging() && mService.isBatteryNotLow()) { return true; } final UidStats uidStats = getUidStats( jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true); return uidStats.runningJobs.contains(jobStatus); } return true; } private static NetworkCapabilities.Builder copyCapabilities( @NonNull final NetworkRequest request) { final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); Loading Loading @@ -717,10 +801,12 @@ public final class ConnectivityController extends RestrictingController implemen // Second, is the network congested? if (isCongestionDelayed(jobStatus, network, capabilities, constants)) return false; // Third, is the network a strict match? if (!isStrongEnough(jobStatus, capabilities, constants)) return false; // Is the network a strict match? if (isStrictSatisfied(jobStatus, network, capabilities, constants)) return true; // Third, is the network a relaxed match? // Is the network a relaxed match? if (isRelaxedSatisfied(jobStatus, network, capabilities, constants)) return true; return false; Loading Loading @@ -985,6 +1071,24 @@ public final class ConnectivityController extends RestrictingController implemen return changed; } @GuardedBy("mLock") private void updateAllTrackedJobsLocked(boolean allowThrottle) { if (allowThrottle) { final long throttleTimeLeftMs = (mLastAllJobUpdateTimeElapsed + mConstants.CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS) - sElapsedRealtimeClock.millis(); if (throttleTimeLeftMs > 0) { Message msg = mHandler.obtainMessage(MSG_UPDATE_ALL_TRACKED_JOBS, 1, 0); mHandler.sendMessageDelayed(msg, throttleTimeLeftMs); return; } } mHandler.removeMessages(MSG_UPDATE_ALL_TRACKED_JOBS); updateTrackedJobsLocked(-1, null); mLastAllJobUpdateTimeElapsed = sElapsedRealtimeClock.millis(); } /** * Update any jobs tracked by this controller that match given filters. * Loading Loading @@ -1088,7 +1192,11 @@ public final class ConnectivityController extends RestrictingController implemen Slog.v(TAG, "onCapabilitiesChanged: " + network); } synchronized (mLock) { mAvailableNetworks.put(network, capabilities); final NetworkCapabilities oldCaps = mAvailableNetworks.put(network, capabilities); if (oldCaps != null) { maybeUnregisterSignalStrengthCallbackLocked(oldCaps); } maybeRegisterSignalStrengthCallbackLocked(capabilities); updateTrackedJobsLocked(-1, network); postAdjustCallbacks(); } Loading @@ -1100,7 +1208,10 @@ public final class ConnectivityController extends RestrictingController implemen Slog.v(TAG, "onLost: " + network); } synchronized (mLock) { mAvailableNetworks.remove(network); final NetworkCapabilities capabilities = mAvailableNetworks.remove(network); if (capabilities != null) { maybeUnregisterSignalStrengthCallbackLocked(capabilities); } for (int u = 0; u < mCurrentDefaultNetworkCallbacks.size(); ++u) { UidDefaultNetworkCallback callback = mCurrentDefaultNetworkCallbacks.valueAt(u); if (Objects.equals(callback.mDefaultNetwork, network)) { Loading @@ -1111,6 +1222,63 @@ public final class ConnectivityController extends RestrictingController implemen postAdjustCallbacks(); } } @GuardedBy("mLock") private void maybeRegisterSignalStrengthCallbackLocked( @NonNull NetworkCapabilities capabilities) { if (!capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { return; } TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); final Set<Integer> subscriptionIds = capabilities.getSubscriptionIds(); for (int subId : subscriptionIds) { if (mSignalStrengths.indexOfKey(subId) >= 0) { continue; } TelephonyManager idTm = telephonyManager.createForSubscriptionId(subId); CellSignalStrengthCallback callback = new CellSignalStrengthCallback(); idTm.registerTelephonyCallback( JobSchedulerBackgroundThread.getExecutor(), callback); mSignalStrengths.put(subId, callback); final SignalStrength signalStrength = idTm.getSignalStrength(); if (signalStrength != null) { callback.signalStrength = signalStrength.getLevel(); } } } @GuardedBy("mLock") private void maybeUnregisterSignalStrengthCallbackLocked( @NonNull NetworkCapabilities capabilities) { if (!capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { return; } ArraySet<Integer> activeIds = new ArraySet<>(); for (int i = 0, size = mAvailableNetworks.size(); i < size; ++i) { NetworkCapabilities nc = mAvailableNetworks.valueAt(i); if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { activeIds.addAll(nc.getSubscriptionIds()); } } if (DEBUG) { Slog.d(TAG, "Active subscription IDs: " + activeIds); } TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); Set<Integer> subscriptionIds = capabilities.getSubscriptionIds(); for (int subId : subscriptionIds) { if (activeIds.contains(subId)) { continue; } TelephonyManager idTm = telephonyManager.createForSubscriptionId(subId); CellSignalStrengthCallback callback = mSignalStrengths.removeReturnOld(subId); if (callback != null) { idTm.unregisterTelephonyCallback(callback); } else { Slog.wtf(TAG, "Callback for sub " + subId + " didn't exist?!?!"); } } } }; private class CcHandler extends Handler { Loading @@ -1127,6 +1295,13 @@ public final class ConnectivityController extends RestrictingController implemen maybeAdjustRegisteredCallbacksLocked(); } break; case MSG_UPDATE_ALL_TRACKED_JOBS: synchronized (mLock) { final boolean allowThrottle = msg.arg1 == 1; updateAllTrackedJobsLocked(allowThrottle); } break; } } } Loading Loading @@ -1268,6 +1443,33 @@ public final class ConnectivityController extends RestrictingController implemen } } private class CellSignalStrengthCallback extends TelephonyCallback implements TelephonyCallback.SignalStrengthsListener { @GuardedBy("mLock") public int signalStrength = CellSignalStrength.SIGNAL_STRENGTH_GREAT; @Override public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) { synchronized (mLock) { final int newSignalStrength = signalStrength.getLevel(); if (DEBUG) { Slog.d(TAG, "Signal strength changing from " + this.signalStrength + " to " + newSignalStrength); for (CellSignalStrength css : signalStrength.getCellSignalStrengths()) { Slog.d(TAG, "CSS: " + css.getLevel() + " " + css); } } if (this.signalStrength == newSignalStrength) { // This happens a lot. return; } this.signalStrength = newSignalStrength; // Update job bookkeeping out of band to avoid blocking callback progress. mHandler.obtainMessage(MSG_UPDATE_ALL_TRACKED_JOBS, 1, 0).sendToTarget(); } } } @GuardedBy("mLock") @Override public void dumpControllerStateLocked(IndentingPrintWriter pw, Loading Loading @@ -1299,6 +1501,20 @@ public final class ConnectivityController extends RestrictingController implemen } pw.println(); if (mSignalStrengths.size() > 0) { pw.println("Subscription ID signal strengths:"); pw.increaseIndent(); for (int i = 0; i < mSignalStrengths.size(); ++i) { pw.print(mSignalStrengths.keyAt(i)); pw.print(": "); pw.println(mSignalStrengths.valueAt(i).signalStrength); } pw.decreaseIndent(); } else { pw.println("No cached signal strengths"); } pw.println(); pw.println("Current default network callbacks:"); pw.increaseIndent(); for (int i = 0; i < mCurrentDefaultNetworkCallbacks.size(); i++) { Loading apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +4 −3 Original line number Diff line number Diff line Loading @@ -1139,11 +1139,12 @@ public final class JobStatus { */ public float getFractionRunTime() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); if (earliestRunTimeElapsedMillis == 0 && latestRunTimeElapsedMillis == Long.MAX_VALUE) { if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME && latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) { return 1; } else if (earliestRunTimeElapsedMillis == 0) { } else if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME) { return now >= latestRunTimeElapsedMillis ? 1 : 0; } else if (latestRunTimeElapsedMillis == Long.MAX_VALUE) { } else if (latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) { return now >= earliestRunTimeElapsedMillis ? 1 : 0; } else { if (now <= earliestRunTimeElapsedMillis) { Loading services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +427 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +46 −0 Original line number Diff line number Diff line Loading @@ -417,6 +417,9 @@ public class JobSchedulerService extends com.android.server.SystemService break; case Constants.KEY_CONN_CONGESTION_DELAY_FRAC: case Constants.KEY_CONN_PREFETCH_RELAX_FRAC: case Constants.KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC: case Constants.KEY_CONN_USE_CELL_SIGNAL_STRENGTH: case Constants.KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS: mConstants.updateConnectivityConstantsLocked(); break; case Constants.KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS: Loading Loading @@ -489,6 +492,12 @@ public class JobSchedulerService extends com.android.server.SystemService private static final String KEY_MIN_EXP_BACKOFF_TIME_MS = "min_exp_backoff_time_ms"; private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac"; private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac"; private static final String KEY_CONN_USE_CELL_SIGNAL_STRENGTH = "conn_use_cell_signal_strength"; private static final String KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS = "conn_update_all_jobs_min_interval_ms"; private static final String KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC = "conn_low_signal_strength_relax_frac"; private static final String KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS = "prefetch_force_batch_relax_threshold_ms"; private static final String KEY_ENABLE_API_QUOTAS = "enable_api_quotas"; Loading @@ -514,6 +523,9 @@ public class JobSchedulerService extends com.android.server.SystemService private static final long DEFAULT_MIN_EXP_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS; private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f; private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f; private static final boolean DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH = true; private static final long DEFAULT_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS = MINUTE_IN_MILLIS; private static final float DEFAULT_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC = 0.5f; private static final long DEFAULT_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS = HOUR_IN_MILLIS; private static final boolean DEFAULT_ENABLE_API_QUOTAS = true; private static final int DEFAULT_API_QUOTA_SCHEDULE_COUNT = 250; Loading Loading @@ -569,6 +581,23 @@ public class JobSchedulerService extends com.android.server.SystemService * we consider matching it against a metered network. */ public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC; /** * Whether to use the cell signal strength to determine if a particular job is eligible to * run. */ public boolean CONN_USE_CELL_SIGNAL_STRENGTH = DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH; /** * When throttling updating all tracked jobs, make sure not to update them more frequently * than this value. */ public long CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS = DEFAULT_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS; /** * The fraction of a job's running window that must pass before we consider running it on * low signal strength networks. */ public float CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC = DEFAULT_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC; /** * The amount of time within which we would consider the app to be launching relatively soon Loading Loading @@ -661,6 +690,18 @@ public class JobSchedulerService extends com.android.server.SystemService CONN_PREFETCH_RELAX_FRAC = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_CONN_PREFETCH_RELAX_FRAC, DEFAULT_CONN_PREFETCH_RELAX_FRAC); CONN_USE_CELL_SIGNAL_STRENGTH = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_CONN_USE_CELL_SIGNAL_STRENGTH, DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH); CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS = DeviceConfig.getLong( DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS, DEFAULT_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS); CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC = DeviceConfig.getFloat( DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC, DEFAULT_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC); } private void updatePrefetchConstantsLocked() { Loading Loading @@ -739,6 +780,11 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println(); pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); pw.print(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println(); pw.print(KEY_CONN_USE_CELL_SIGNAL_STRENGTH, CONN_USE_CELL_SIGNAL_STRENGTH).println(); pw.print(KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS, CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS) .println(); pw.print(KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC, CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC) .println(); pw.print(KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS, PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS).println(); Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +225 −9 Original line number Diff line number Diff line Loading @@ -35,6 +35,10 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.UserHandle; import android.telephony.CellSignalStrength; import android.telephony.SignalStrength; import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; Loading @@ -60,6 +64,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.Predicate; /** Loading Loading @@ -213,9 +218,16 @@ public final class ConnectivityController extends RestrictingController implemen * is only done in {@link #maybeAdjustRegisteredCallbacksLocked()} and may sometimes be stale. */ private final List<UidStats> mSortedStats = new ArrayList<>(); @GuardedBy("mLock") private long mLastCallbackAdjustmentTimeElapsed; @GuardedBy("mLock") private final SparseArray<CellSignalStrengthCallback> mSignalStrengths = new SparseArray<>(); @GuardedBy("mLock") private long mLastAllJobUpdateTimeElapsed; private static final int MSG_ADJUST_CALLBACKS = 0; private static final int MSG_UPDATE_ALL_TRACKED_JOBS = 1; private final Handler mHandler; Loading Loading @@ -529,11 +541,7 @@ public final class ConnectivityController extends RestrictingController implemen @GuardedBy("mLock") public void onBatteryStateChangedLocked() { // Update job bookkeeping out of band to avoid blocking broadcast progress. JobSchedulerBackgroundThread.getHandler().post(() -> { synchronized (mLock) { updateTrackedJobsLocked(-1, null); } }); mHandler.sendEmptyMessage(MSG_UPDATE_ALL_TRACKED_JOBS); } private boolean isUsable(NetworkCapabilities capabilities) { Loading Loading @@ -650,6 +658,82 @@ public final class ConnectivityController extends RestrictingController implemen } } @GuardedBy("mLock") private boolean isStrongEnough(JobStatus jobStatus, NetworkCapabilities capabilities, Constants constants) { final int priority = jobStatus.getEffectivePriority(); if (priority >= JobInfo.PRIORITY_HIGH) { return true; } if (!constants.CONN_USE_CELL_SIGNAL_STRENGTH) { return true; } if (!capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { return true; } if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { // Exclude VPNs because it's currently not possible to determine the VPN's underlying // network, and thus the correct signal strength of the VPN's network. // Transmitting data over a VPN is generally more battery-expensive than on the // underlying network, so: // TODO: find a good way to reduce job use of VPN when it'll be very expensive // For now, we just pretend VPNs are always strong enough return true; } // VCNs running over WiFi will declare TRANSPORT_CELLULAR. When connected, a VCN will // most likely be the default network. We ideally don't want this to restrict jobs when the // VCN incorrectly declares the CELLULAR transport, but there's currently no way to // determine if a network is a VCN. When there is: // TODO(216127782): exclude VCN running over WiFi from this check int signalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; // Use the best strength found. final Set<Integer> subscriptionIds = capabilities.getSubscriptionIds(); for (int subId : subscriptionIds) { CellSignalStrengthCallback callback = mSignalStrengths.get(subId); if (callback != null) { signalStrength = Math.max(signalStrength, callback.signalStrength); } else { Slog.wtf(TAG, "Subscription ID " + subId + " doesn't have a registered callback"); } } if (DEBUG) { Slog.d(TAG, "Cell signal strength for job=" + signalStrength); } // Treat "NONE_OR_UNKNOWN" as "NONE". if (signalStrength <= CellSignalStrength.SIGNAL_STRENGTH_POOR) { // If signal strength is poor, don't run MIN or LOW priority jobs, and only // run DEFAULT priority jobs if the device is charging or the job has been waiting // long enough. if (priority > JobInfo.PRIORITY_DEFAULT) { return true; } if (priority < JobInfo.PRIORITY_DEFAULT) { return false; } // DEFAULT job. return (mService.isBatteryCharging() && mService.isBatteryNotLow()) || jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC; } if (signalStrength <= CellSignalStrength.SIGNAL_STRENGTH_MODERATE) { // If signal strength is moderate, only run MIN priority jobs when the device // is charging, or the job is already running. if (priority >= JobInfo.PRIORITY_LOW) { return true; } // MIN job. if (mService.isBatteryCharging() && mService.isBatteryNotLow()) { return true; } final UidStats uidStats = getUidStats( jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true); return uidStats.runningJobs.contains(jobStatus); } return true; } private static NetworkCapabilities.Builder copyCapabilities( @NonNull final NetworkRequest request) { final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); Loading Loading @@ -717,10 +801,12 @@ public final class ConnectivityController extends RestrictingController implemen // Second, is the network congested? if (isCongestionDelayed(jobStatus, network, capabilities, constants)) return false; // Third, is the network a strict match? if (!isStrongEnough(jobStatus, capabilities, constants)) return false; // Is the network a strict match? if (isStrictSatisfied(jobStatus, network, capabilities, constants)) return true; // Third, is the network a relaxed match? // Is the network a relaxed match? if (isRelaxedSatisfied(jobStatus, network, capabilities, constants)) return true; return false; Loading Loading @@ -985,6 +1071,24 @@ public final class ConnectivityController extends RestrictingController implemen return changed; } @GuardedBy("mLock") private void updateAllTrackedJobsLocked(boolean allowThrottle) { if (allowThrottle) { final long throttleTimeLeftMs = (mLastAllJobUpdateTimeElapsed + mConstants.CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS) - sElapsedRealtimeClock.millis(); if (throttleTimeLeftMs > 0) { Message msg = mHandler.obtainMessage(MSG_UPDATE_ALL_TRACKED_JOBS, 1, 0); mHandler.sendMessageDelayed(msg, throttleTimeLeftMs); return; } } mHandler.removeMessages(MSG_UPDATE_ALL_TRACKED_JOBS); updateTrackedJobsLocked(-1, null); mLastAllJobUpdateTimeElapsed = sElapsedRealtimeClock.millis(); } /** * Update any jobs tracked by this controller that match given filters. * Loading Loading @@ -1088,7 +1192,11 @@ public final class ConnectivityController extends RestrictingController implemen Slog.v(TAG, "onCapabilitiesChanged: " + network); } synchronized (mLock) { mAvailableNetworks.put(network, capabilities); final NetworkCapabilities oldCaps = mAvailableNetworks.put(network, capabilities); if (oldCaps != null) { maybeUnregisterSignalStrengthCallbackLocked(oldCaps); } maybeRegisterSignalStrengthCallbackLocked(capabilities); updateTrackedJobsLocked(-1, network); postAdjustCallbacks(); } Loading @@ -1100,7 +1208,10 @@ public final class ConnectivityController extends RestrictingController implemen Slog.v(TAG, "onLost: " + network); } synchronized (mLock) { mAvailableNetworks.remove(network); final NetworkCapabilities capabilities = mAvailableNetworks.remove(network); if (capabilities != null) { maybeUnregisterSignalStrengthCallbackLocked(capabilities); } for (int u = 0; u < mCurrentDefaultNetworkCallbacks.size(); ++u) { UidDefaultNetworkCallback callback = mCurrentDefaultNetworkCallbacks.valueAt(u); if (Objects.equals(callback.mDefaultNetwork, network)) { Loading @@ -1111,6 +1222,63 @@ public final class ConnectivityController extends RestrictingController implemen postAdjustCallbacks(); } } @GuardedBy("mLock") private void maybeRegisterSignalStrengthCallbackLocked( @NonNull NetworkCapabilities capabilities) { if (!capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { return; } TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); final Set<Integer> subscriptionIds = capabilities.getSubscriptionIds(); for (int subId : subscriptionIds) { if (mSignalStrengths.indexOfKey(subId) >= 0) { continue; } TelephonyManager idTm = telephonyManager.createForSubscriptionId(subId); CellSignalStrengthCallback callback = new CellSignalStrengthCallback(); idTm.registerTelephonyCallback( JobSchedulerBackgroundThread.getExecutor(), callback); mSignalStrengths.put(subId, callback); final SignalStrength signalStrength = idTm.getSignalStrength(); if (signalStrength != null) { callback.signalStrength = signalStrength.getLevel(); } } } @GuardedBy("mLock") private void maybeUnregisterSignalStrengthCallbackLocked( @NonNull NetworkCapabilities capabilities) { if (!capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { return; } ArraySet<Integer> activeIds = new ArraySet<>(); for (int i = 0, size = mAvailableNetworks.size(); i < size; ++i) { NetworkCapabilities nc = mAvailableNetworks.valueAt(i); if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { activeIds.addAll(nc.getSubscriptionIds()); } } if (DEBUG) { Slog.d(TAG, "Active subscription IDs: " + activeIds); } TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); Set<Integer> subscriptionIds = capabilities.getSubscriptionIds(); for (int subId : subscriptionIds) { if (activeIds.contains(subId)) { continue; } TelephonyManager idTm = telephonyManager.createForSubscriptionId(subId); CellSignalStrengthCallback callback = mSignalStrengths.removeReturnOld(subId); if (callback != null) { idTm.unregisterTelephonyCallback(callback); } else { Slog.wtf(TAG, "Callback for sub " + subId + " didn't exist?!?!"); } } } }; private class CcHandler extends Handler { Loading @@ -1127,6 +1295,13 @@ public final class ConnectivityController extends RestrictingController implemen maybeAdjustRegisteredCallbacksLocked(); } break; case MSG_UPDATE_ALL_TRACKED_JOBS: synchronized (mLock) { final boolean allowThrottle = msg.arg1 == 1; updateAllTrackedJobsLocked(allowThrottle); } break; } } } Loading Loading @@ -1268,6 +1443,33 @@ public final class ConnectivityController extends RestrictingController implemen } } private class CellSignalStrengthCallback extends TelephonyCallback implements TelephonyCallback.SignalStrengthsListener { @GuardedBy("mLock") public int signalStrength = CellSignalStrength.SIGNAL_STRENGTH_GREAT; @Override public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) { synchronized (mLock) { final int newSignalStrength = signalStrength.getLevel(); if (DEBUG) { Slog.d(TAG, "Signal strength changing from " + this.signalStrength + " to " + newSignalStrength); for (CellSignalStrength css : signalStrength.getCellSignalStrengths()) { Slog.d(TAG, "CSS: " + css.getLevel() + " " + css); } } if (this.signalStrength == newSignalStrength) { // This happens a lot. return; } this.signalStrength = newSignalStrength; // Update job bookkeeping out of band to avoid blocking callback progress. mHandler.obtainMessage(MSG_UPDATE_ALL_TRACKED_JOBS, 1, 0).sendToTarget(); } } } @GuardedBy("mLock") @Override public void dumpControllerStateLocked(IndentingPrintWriter pw, Loading Loading @@ -1299,6 +1501,20 @@ public final class ConnectivityController extends RestrictingController implemen } pw.println(); if (mSignalStrengths.size() > 0) { pw.println("Subscription ID signal strengths:"); pw.increaseIndent(); for (int i = 0; i < mSignalStrengths.size(); ++i) { pw.print(mSignalStrengths.keyAt(i)); pw.print(": "); pw.println(mSignalStrengths.valueAt(i).signalStrength); } pw.decreaseIndent(); } else { pw.println("No cached signal strengths"); } pw.println(); pw.println("Current default network callbacks:"); pw.increaseIndent(); for (int i = 0; i < mCurrentDefaultNetworkCallbacks.size(); i++) { Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +4 −3 Original line number Diff line number Diff line Loading @@ -1139,11 +1139,12 @@ public final class JobStatus { */ public float getFractionRunTime() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); if (earliestRunTimeElapsedMillis == 0 && latestRunTimeElapsedMillis == Long.MAX_VALUE) { if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME && latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) { return 1; } else if (earliestRunTimeElapsedMillis == 0) { } else if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME) { return now >= latestRunTimeElapsedMillis ? 1 : 0; } else if (latestRunTimeElapsedMillis == Long.MAX_VALUE) { } else if (latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) { return now >= earliestRunTimeElapsedMillis ? 1 : 0; } else { if (now <= earliestRunTimeElapsedMillis) { Loading
services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +427 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes