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

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

Merge "Run "insane" network jobs when charging + unmetered." into sc-dev

parents dfe917dc 093a6dc5
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -1229,7 +1229,11 @@ public class JobInfo implements Parcelable {
         * </ul>
         * Note that the system may choose to delay jobs with large network
         * usage estimates when the device has a poor network connection, in
         * order to save battery.
         * order to save battery and possible network costs.
         * Starting from Android version {@link Build.VERSION_CODES#S}, JobScheduler may attempt
         * to run large jobs when the device is charging and on an unmetered network, even if the
         * network is slow. This gives large jobs an opportunity to make forward progress, even if
         * they risk timing out.
         * <p>
         * The values provided here only reflect the traffic that will be
         * performed by the base job; if you're using {@link JobWorkItem} then
+67 −2
Original line number Diff line number Diff line
@@ -25,12 +25,18 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.job.JobInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkPolicyManager;
import android.net.NetworkRequest;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -101,6 +107,8 @@ public final class ConnectivityController extends RestrictingController implemen
    private final ConnectivityManager mConnManager;
    private final NetworkPolicyManagerInternal mNetPolicyManagerInternal;

    private final ChargingTracker mChargingTracker;

    /** List of tracked jobs keyed by source UID. */
    @GuardedBy("mLock")
    private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>();
@@ -229,6 +237,9 @@ public final class ConnectivityController extends RestrictingController implemen
        // network changes against the active network for each UID with jobs.
        final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
        mConnManager.registerNetworkCallback(request, mNetworkCallback);

        mChargingTracker = new ChargingTracker();
        mChargingTracker.startTracking();
    }

    @GuardedBy("mLock")
@@ -538,6 +549,14 @@ public final class ConnectivityController extends RestrictingController implemen
     */
    private boolean isInsane(JobStatus jobStatus, Network network,
            NetworkCapabilities capabilities, Constants constants) {
        if (capabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
                && mChargingTracker.isCharging()) {
            // We're charging and on an unmetered network. We don't have to be as conservative about
            // making sure the job will run within its max execution time. Let's just hope the app
            // supports interruptible work.
            return false;
        }

        // Use the maximum possible time since it gives us an upper bound, even though the job
        // could end up stopping earlier.
        final long maxJobExecutionTimeMs = mService.getMaxJobExecutionTimeMs(jobStatus);
@@ -922,7 +941,7 @@ public final class ConnectivityController extends RestrictingController implemen
     *                      {@link Network}, or {@code null} to update all tracked jobs.
     */
    @GuardedBy("mLock")
    private void updateTrackedJobsLocked(int filterUid, Network filterNetwork) {
    private void updateTrackedJobsLocked(int filterUid, @Nullable Network filterNetwork) {
        boolean changed = false;
        if (filterUid == -1) {
            for (int i = mTrackedJobs.size() - 1; i >= 0; i--) {
@@ -937,7 +956,8 @@ public final class ConnectivityController extends RestrictingController implemen
    }

    @GuardedBy("mLock")
    private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork) {
    private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs,
            @Nullable Network filterNetwork) {
        if (jobs == null || jobs.size() == 0) {
            return false;
        }
@@ -993,6 +1013,51 @@ public final class ConnectivityController extends RestrictingController implemen
        }
    }

    private final class ChargingTracker extends BroadcastReceiver {
        /**
         * Track whether we're "charging", where charging means that we're ready to commit to
         * doing work.
         */
        private boolean mCharging;

        ChargingTracker() {}

        public void startTracking() {
            IntentFilter filter = new IntentFilter();
            filter.addAction(BatteryManager.ACTION_CHARGING);
            filter.addAction(BatteryManager.ACTION_DISCHARGING);
            mContext.registerReceiver(this, filter);

            // Initialise tracker state.
            final BatteryManagerInternal batteryManagerInternal =
                    LocalServices.getService(BatteryManagerInternal.class);
            mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
        }

        public boolean isCharging() {
            return mCharging;
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            synchronized (mLock) {
                final String action = intent.getAction();
                if (BatteryManager.ACTION_CHARGING.equals(action)) {
                    if (mCharging) {
                        return;
                    }
                    mCharging = true;
                } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
                    if (!mCharging) {
                        return;
                    }
                    mCharging = false;
                }
                updateTrackedJobsLocked(-1, null);
            }
        }
    }

    private final NetworkCallback mNetworkCallback = new NetworkCallback() {
        @Override
        public void onAvailable(Network network) {
+39 −0
Original line number Diff line number Diff line
@@ -48,14 +48,18 @@ import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.job.JobInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManagerInternal;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkPolicyManager;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.Build;
import android.os.Looper;
import android.os.SystemClock;
@@ -70,6 +74,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@@ -83,6 +88,8 @@ public class ConnectivityControllerTest {
    @Mock
    private Context mContext;
    @Mock
    private BatteryManagerInternal mBatteryManagerInternal;
    @Mock
    private ConnectivityManager mConnManager;
    @Mock
    private NetworkPolicyManager mNetPolicyManager;
@@ -108,6 +115,9 @@ public class ConnectivityControllerTest {
        LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
        LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal);

        LocalServices.removeServiceForTest(BatteryManagerInternal.class);
        LocalServices.addService(BatteryManagerInternal.class, mBatteryManagerInternal);

        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());

        // Freeze the clocks at this moment in time
@@ -143,8 +153,18 @@ public class ConnectivityControllerTest {
                        DataUnit.MEBIBYTES.toBytes(1))
                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);

        final ArgumentCaptor<BroadcastReceiver> chargingCaptor =
                ArgumentCaptor.forClass(BroadcastReceiver.class);
        when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
                .thenReturn(false);
        final ConnectivityController controller = new ConnectivityController(mService);
        verify(mContext).registerReceiver(chargingCaptor.capture(),
                ArgumentMatchers.argThat(filter ->
                        filter.hasAction(BatteryManager.ACTION_CHARGING)
                                && filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
        when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(10 * 60_000L);
        final BroadcastReceiver chargingReceiver = chargingCaptor.getValue();
        chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING));

        // Slow network is too slow
        assertFalse(controller.isSatisfied(createJobStatus(job), net,
@@ -166,7 +186,18 @@ public class ConnectivityControllerTest {
        assertTrue(controller.isSatisfied(createJobStatus(job), net,
                createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
                        .setLinkDownstreamBandwidthKbps(130).build(), mConstants));
        // Slow network is too slow, but device is charging and network is unmetered.
        when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
                .thenReturn(true);
        chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING));
        assertTrue(controller.isSatisfied(createJobStatus(job), net,
                createCapabilitiesBuilder().addCapability(NET_CAPABILITY_NOT_METERED)
                        .setLinkUpstreamBandwidthKbps(1).setLinkDownstreamBandwidthKbps(1).build(),
                mConstants));

        when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
                .thenReturn(false);
        chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING));
        when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(60_000L);

        // Slow network is too slow
@@ -189,6 +220,14 @@ public class ConnectivityControllerTest {
        assertFalse(controller.isSatisfied(createJobStatus(job), net,
                createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
                        .setLinkDownstreamBandwidthKbps(130).build(), mConstants));
        // Slow network is too slow, but device is charging and network is unmetered.
        when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
                .thenReturn(true);
        chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING));
        assertTrue(controller.isSatisfied(createJobStatus(job), net,
                createCapabilitiesBuilder().addCapability(NET_CAPABILITY_NOT_METERED)
                        .setLinkUpstreamBandwidthKbps(1).setLinkDownstreamBandwidthKbps(1).build(),
                mConstants));
    }

    @Test