Merge "Run "insane" network jobs when charging + unmetered." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
a75009f504
@@ -1229,7 +1229,11 @@ public class JobInfo implements Parcelable {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* Note that the system may choose to delay jobs with large network
|
* Note that the system may choose to delay jobs with large network
|
||||||
* usage estimates when the device has a poor network connection, in
|
* 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>
|
* <p>
|
||||||
* The values provided here only reflect the traffic that will be
|
* The values provided here only reflect the traffic that will be
|
||||||
* performed by the base job; if you're using {@link JobWorkItem} then
|
* performed by the base job; if you're using {@link JobWorkItem} then
|
||||||
|
|||||||
@@ -25,12 +25,18 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
|
|||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.job.JobInfo;
|
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;
|
||||||
import android.net.ConnectivityManager.NetworkCallback;
|
import android.net.ConnectivityManager.NetworkCallback;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
import android.net.NetworkPolicyManager;
|
import android.net.NetworkPolicyManager;
|
||||||
import android.net.NetworkRequest;
|
import android.net.NetworkRequest;
|
||||||
|
import android.os.BatteryManager;
|
||||||
|
import android.os.BatteryManagerInternal;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
@@ -101,6 +107,8 @@ public final class ConnectivityController extends RestrictingController implemen
|
|||||||
private final ConnectivityManager mConnManager;
|
private final ConnectivityManager mConnManager;
|
||||||
private final NetworkPolicyManagerInternal mNetPolicyManagerInternal;
|
private final NetworkPolicyManagerInternal mNetPolicyManagerInternal;
|
||||||
|
|
||||||
|
private final ChargingTracker mChargingTracker;
|
||||||
|
|
||||||
/** List of tracked jobs keyed by source UID. */
|
/** List of tracked jobs keyed by source UID. */
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>();
|
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.
|
// network changes against the active network for each UID with jobs.
|
||||||
final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
|
final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
|
||||||
mConnManager.registerNetworkCallback(request, mNetworkCallback);
|
mConnManager.registerNetworkCallback(request, mNetworkCallback);
|
||||||
|
|
||||||
|
mChargingTracker = new ChargingTracker();
|
||||||
|
mChargingTracker.startTracking();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
@@ -538,6 +549,14 @@ public final class ConnectivityController extends RestrictingController implemen
|
|||||||
*/
|
*/
|
||||||
private boolean isInsane(JobStatus jobStatus, Network network,
|
private boolean isInsane(JobStatus jobStatus, Network network,
|
||||||
NetworkCapabilities capabilities, Constants constants) {
|
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
|
// Use the maximum possible time since it gives us an upper bound, even though the job
|
||||||
// could end up stopping earlier.
|
// could end up stopping earlier.
|
||||||
final long maxJobExecutionTimeMs = mService.getMaxJobExecutionTimeMs(jobStatus);
|
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.
|
* {@link Network}, or {@code null} to update all tracked jobs.
|
||||||
*/
|
*/
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private void updateTrackedJobsLocked(int filterUid, Network filterNetwork) {
|
private void updateTrackedJobsLocked(int filterUid, @Nullable Network filterNetwork) {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
if (filterUid == -1) {
|
if (filterUid == -1) {
|
||||||
for (int i = mTrackedJobs.size() - 1; i >= 0; i--) {
|
for (int i = mTrackedJobs.size() - 1; i >= 0; i--) {
|
||||||
@@ -937,7 +956,8 @@ public final class ConnectivityController extends RestrictingController implemen
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@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) {
|
if (jobs == null || jobs.size() == 0) {
|
||||||
return false;
|
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() {
|
private final NetworkCallback mNetworkCallback = new NetworkCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onAvailable(Network network) {
|
public void onAvailable(Network network) {
|
||||||
|
|||||||
@@ -48,14 +48,18 @@ import static org.mockito.Mockito.verify;
|
|||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.job.JobInfo;
|
import android.app.job.JobInfo;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManagerInternal;
|
import android.content.pm.PackageManagerInternal;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.ConnectivityManager.NetworkCallback;
|
import android.net.ConnectivityManager.NetworkCallback;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
import android.net.NetworkPolicyManager;
|
import android.net.NetworkPolicyManager;
|
||||||
|
import android.os.BatteryManager;
|
||||||
|
import android.os.BatteryManagerInternal;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
@@ -70,6 +74,7 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.ArgumentMatchers;
|
||||||
import org.mockito.InOrder;
|
import org.mockito.InOrder;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
@@ -83,6 +88,8 @@ public class ConnectivityControllerTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
@Mock
|
@Mock
|
||||||
|
private BatteryManagerInternal mBatteryManagerInternal;
|
||||||
|
@Mock
|
||||||
private ConnectivityManager mConnManager;
|
private ConnectivityManager mConnManager;
|
||||||
@Mock
|
@Mock
|
||||||
private NetworkPolicyManager mNetPolicyManager;
|
private NetworkPolicyManager mNetPolicyManager;
|
||||||
@@ -108,6 +115,9 @@ public class ConnectivityControllerTest {
|
|||||||
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
|
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
|
||||||
LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal);
|
LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal);
|
||||||
|
|
||||||
|
LocalServices.removeServiceForTest(BatteryManagerInternal.class);
|
||||||
|
LocalServices.addService(BatteryManagerInternal.class, mBatteryManagerInternal);
|
||||||
|
|
||||||
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
|
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
|
||||||
|
|
||||||
// Freeze the clocks at this moment in time
|
// Freeze the clocks at this moment in time
|
||||||
@@ -143,8 +153,18 @@ public class ConnectivityControllerTest {
|
|||||||
DataUnit.MEBIBYTES.toBytes(1))
|
DataUnit.MEBIBYTES.toBytes(1))
|
||||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
|
.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);
|
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);
|
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
|
// Slow network is too slow
|
||||||
assertFalse(controller.isSatisfied(createJobStatus(job), net,
|
assertFalse(controller.isSatisfied(createJobStatus(job), net,
|
||||||
@@ -166,7 +186,18 @@ public class ConnectivityControllerTest {
|
|||||||
assertTrue(controller.isSatisfied(createJobStatus(job), net,
|
assertTrue(controller.isSatisfied(createJobStatus(job), net,
|
||||||
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
|
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
|
||||||
.setLinkDownstreamBandwidthKbps(130).build(), mConstants));
|
.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);
|
when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(60_000L);
|
||||||
|
|
||||||
// Slow network is too slow
|
// Slow network is too slow
|
||||||
@@ -189,6 +220,14 @@ public class ConnectivityControllerTest {
|
|||||||
assertFalse(controller.isSatisfied(createJobStatus(job), net,
|
assertFalse(controller.isSatisfied(createJobStatus(job), net,
|
||||||
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
|
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
|
||||||
.setLinkDownstreamBandwidthKbps(130).build(), mConstants));
|
.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
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user