Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 813b8298 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Defer low priority jobs in poor network conditions."

parents 0ef7951d c7d16d6f
Loading
Loading
Loading
Loading
+46 −0
Original line number Diff line number Diff line
@@ -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:
@@ -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";
@@ -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;
@@ -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
@@ -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() {
@@ -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();

+225 −9
Original line number Diff line number Diff line
@@ -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;
@@ -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;

/**
@@ -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;

@@ -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) {
@@ -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();
@@ -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;
@@ -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.
     *
@@ -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();
            }
@@ -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)) {
@@ -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 {
@@ -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;
                }
            }
        }
@@ -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,
@@ -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++) {
+4 −3
Original line number Diff line number Diff line
@@ -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) {
+427 −0

File changed.

Preview size limit exceeded, changes collapsed.