mirror of
https://github.com/androidx/media.git
synced 2025-05-11 01:31:40 +08:00
Merge pull request #7395 from jdegroot-dss:add-storage-not-low-requirement
PiperOrigin-RevId: 313804207
This commit is contained in:
commit
496a315d91
@ -142,6 +142,8 @@
|
|||||||
directly instead.
|
directly instead.
|
||||||
* Update `CachedContentIndex` to use `SecureRandom` for generating the
|
* Update `CachedContentIndex` to use `SecureRandom` for generating the
|
||||||
initialization vector used to encrypt the cache contents.
|
initialization vector used to encrypt the cache contents.
|
||||||
|
* Add `Requirements.DEVICE_STORAGE_NOT_LOW`, which can be specified as a
|
||||||
|
requirement to a `DownloadManager` for it to proceed with downloading.
|
||||||
* Audio:
|
* Audio:
|
||||||
* Add a sample count parameter to `MediaCodecRenderer.processOutputBuffer`
|
* Add a sample count parameter to `MediaCodecRenderer.processOutputBuffer`
|
||||||
and `AudioSink.handleBuffer` to allow batching multiple encoded frames
|
and `AudioSink.handleBuffer` to allow batching multiple encoded frames
|
||||||
|
@ -65,6 +65,11 @@ public final class JobDispatcherScheduler implements Scheduler {
|
|||||||
private static final String KEY_SERVICE_ACTION = "service_action";
|
private static final String KEY_SERVICE_ACTION = "service_action";
|
||||||
private static final String KEY_SERVICE_PACKAGE = "service_package";
|
private static final String KEY_SERVICE_PACKAGE = "service_package";
|
||||||
private static final String KEY_REQUIREMENTS = "requirements";
|
private static final String KEY_REQUIREMENTS = "requirements";
|
||||||
|
private static final int SUPPORTED_REQUIREMENTS =
|
||||||
|
Requirements.NETWORK
|
||||||
|
| Requirements.NETWORK_UNMETERED
|
||||||
|
| Requirements.DEVICE_IDLE
|
||||||
|
| Requirements.DEVICE_CHARGING;
|
||||||
|
|
||||||
private final String jobTag;
|
private final String jobTag;
|
||||||
private final FirebaseJobDispatcher jobDispatcher;
|
private final FirebaseJobDispatcher jobDispatcher;
|
||||||
@ -96,24 +101,35 @@ public final class JobDispatcherScheduler implements Scheduler {
|
|||||||
return result == FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS;
|
return result == FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Requirements getSupportedRequirements(Requirements requirements) {
|
||||||
|
return requirements.filterRequirements(SUPPORTED_REQUIREMENTS);
|
||||||
|
}
|
||||||
|
|
||||||
private static Job buildJob(
|
private static Job buildJob(
|
||||||
FirebaseJobDispatcher dispatcher,
|
FirebaseJobDispatcher dispatcher,
|
||||||
Requirements requirements,
|
Requirements requirements,
|
||||||
String tag,
|
String tag,
|
||||||
String servicePackage,
|
String servicePackage,
|
||||||
String serviceAction) {
|
String serviceAction) {
|
||||||
|
Requirements filteredRequirements = requirements.filterRequirements(SUPPORTED_REQUIREMENTS);
|
||||||
|
if (!filteredRequirements.equals(requirements)) {
|
||||||
|
Log.w(
|
||||||
|
TAG,
|
||||||
|
"Ignoring unsupported requirements: "
|
||||||
|
+ (filteredRequirements.getRequirements() ^ requirements.getRequirements()));
|
||||||
|
}
|
||||||
|
|
||||||
Job.Builder builder =
|
Job.Builder builder =
|
||||||
dispatcher
|
dispatcher
|
||||||
.newJobBuilder()
|
.newJobBuilder()
|
||||||
.setService(JobDispatcherSchedulerService.class) // the JobService that will be called
|
.setService(JobDispatcherSchedulerService.class) // the JobService that will be called
|
||||||
.setTag(tag);
|
.setTag(tag);
|
||||||
|
|
||||||
if (requirements.isUnmeteredNetworkRequired()) {
|
if (requirements.isUnmeteredNetworkRequired()) {
|
||||||
builder.addConstraint(Constraint.ON_UNMETERED_NETWORK);
|
builder.addConstraint(Constraint.ON_UNMETERED_NETWORK);
|
||||||
} else if (requirements.isNetworkRequired()) {
|
} else if (requirements.isNetworkRequired()) {
|
||||||
builder.addConstraint(Constraint.ON_ANY_NETWORK);
|
builder.addConstraint(Constraint.ON_ANY_NETWORK);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requirements.isIdleRequired()) {
|
if (requirements.isIdleRequired()) {
|
||||||
builder.addConstraint(Constraint.DEVICE_IDLE);
|
builder.addConstraint(Constraint.DEVICE_IDLE);
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,12 @@ public final class WorkManagerScheduler implements Scheduler {
|
|||||||
private static final String KEY_SERVICE_ACTION = "service_action";
|
private static final String KEY_SERVICE_ACTION = "service_action";
|
||||||
private static final String KEY_SERVICE_PACKAGE = "service_package";
|
private static final String KEY_SERVICE_PACKAGE = "service_package";
|
||||||
private static final String KEY_REQUIREMENTS = "requirements";
|
private static final String KEY_REQUIREMENTS = "requirements";
|
||||||
|
private static final int SUPPORTED_REQUIREMENTS =
|
||||||
|
Requirements.NETWORK
|
||||||
|
| Requirements.NETWORK_UNMETERED
|
||||||
|
| (Util.SDK_INT >= 23 ? Requirements.DEVICE_IDLE : 0)
|
||||||
|
| Requirements.DEVICE_CHARGING
|
||||||
|
| Requirements.DEVICE_STORAGE_NOT_LOW;
|
||||||
|
|
||||||
private final String workName;
|
private final String workName;
|
||||||
|
|
||||||
@ -70,9 +76,21 @@ public final class WorkManagerScheduler implements Scheduler {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Constraints buildConstraints(Requirements requirements) {
|
@Override
|
||||||
Constraints.Builder builder = new Constraints.Builder();
|
public Requirements getSupportedRequirements(Requirements requirements) {
|
||||||
|
return requirements.filterRequirements(SUPPORTED_REQUIREMENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Constraints buildConstraints(Requirements requirements) {
|
||||||
|
Requirements filteredRequirements = requirements.filterRequirements(SUPPORTED_REQUIREMENTS);
|
||||||
|
if (!filteredRequirements.equals(requirements)) {
|
||||||
|
Log.w(
|
||||||
|
TAG,
|
||||||
|
"Ignoring unsupported requirements: "
|
||||||
|
+ (filteredRequirements.getRequirements() ^ requirements.getRequirements()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Constraints.Builder builder = new Constraints.Builder();
|
||||||
if (requirements.isUnmeteredNetworkRequired()) {
|
if (requirements.isUnmeteredNetworkRequired()) {
|
||||||
builder.setRequiredNetworkType(NetworkType.UNMETERED);
|
builder.setRequiredNetworkType(NetworkType.UNMETERED);
|
||||||
} else if (requirements.isNetworkRequired()) {
|
} else if (requirements.isNetworkRequired()) {
|
||||||
@ -80,13 +98,14 @@ public final class WorkManagerScheduler implements Scheduler {
|
|||||||
} else {
|
} else {
|
||||||
builder.setRequiredNetworkType(NetworkType.NOT_REQUIRED);
|
builder.setRequiredNetworkType(NetworkType.NOT_REQUIRED);
|
||||||
}
|
}
|
||||||
|
if (Util.SDK_INT >= 23 && requirements.isIdleRequired()) {
|
||||||
|
setRequiresDeviceIdle(builder);
|
||||||
|
}
|
||||||
if (requirements.isChargingRequired()) {
|
if (requirements.isChargingRequired()) {
|
||||||
builder.setRequiresCharging(true);
|
builder.setRequiresCharging(true);
|
||||||
}
|
}
|
||||||
|
if (requirements.isStorageNotLowRequired()) {
|
||||||
if (requirements.isIdleRequired() && Util.SDK_INT >= 23) {
|
builder.setRequiresStorageNotLow(true);
|
||||||
setRequiresDeviceIdle(builder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
|
@ -658,6 +658,22 @@ public abstract class DownloadService extends Service {
|
|||||||
if (requirements == null) {
|
if (requirements == null) {
|
||||||
Log.e(TAG, "Ignored SET_REQUIREMENTS: Missing " + KEY_REQUIREMENTS + " extra");
|
Log.e(TAG, "Ignored SET_REQUIREMENTS: Missing " + KEY_REQUIREMENTS + " extra");
|
||||||
} else {
|
} else {
|
||||||
|
@Nullable Scheduler scheduler = getScheduler();
|
||||||
|
if (scheduler != null) {
|
||||||
|
Requirements supportedRequirements = scheduler.getSupportedRequirements(requirements);
|
||||||
|
if (!supportedRequirements.equals(requirements)) {
|
||||||
|
Log.w(
|
||||||
|
TAG,
|
||||||
|
"Ignoring requirements not supported by the Scheduler: "
|
||||||
|
+ (requirements.getRequirements() ^ supportedRequirements.getRequirements()));
|
||||||
|
// We need to make sure DownloadManager only uses requirements supported by the
|
||||||
|
// Scheduler. If we don't do this, DownloadManager can report itself as idle due to an
|
||||||
|
// unmet requirement that the Scheduler doesn't support. This can then lead to the
|
||||||
|
// service being destroyed, even though the Scheduler won't be able to restart it when
|
||||||
|
// the requirement is subsequently met.
|
||||||
|
requirements = supportedRequirements;
|
||||||
|
}
|
||||||
|
}
|
||||||
downloadManager.setRequirements(requirements);
|
downloadManager.setRequirements(requirements);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -50,6 +50,12 @@ public final class PlatformScheduler implements Scheduler {
|
|||||||
private static final String KEY_SERVICE_ACTION = "service_action";
|
private static final String KEY_SERVICE_ACTION = "service_action";
|
||||||
private static final String KEY_SERVICE_PACKAGE = "service_package";
|
private static final String KEY_SERVICE_PACKAGE = "service_package";
|
||||||
private static final String KEY_REQUIREMENTS = "requirements";
|
private static final String KEY_REQUIREMENTS = "requirements";
|
||||||
|
private static final int SUPPORTED_REQUIREMENTS =
|
||||||
|
Requirements.NETWORK
|
||||||
|
| Requirements.NETWORK_UNMETERED
|
||||||
|
| Requirements.DEVICE_IDLE
|
||||||
|
| Requirements.DEVICE_CHARGING
|
||||||
|
| (Util.SDK_INT >= 26 ? Requirements.DEVICE_STORAGE_NOT_LOW : 0);
|
||||||
|
|
||||||
private final int jobId;
|
private final int jobId;
|
||||||
private final ComponentName jobServiceComponentName;
|
private final ComponentName jobServiceComponentName;
|
||||||
@ -86,6 +92,11 @@ public final class PlatformScheduler implements Scheduler {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Requirements getSupportedRequirements(Requirements requirements) {
|
||||||
|
return requirements.filterRequirements(SUPPORTED_REQUIREMENTS);
|
||||||
|
}
|
||||||
|
|
||||||
// @RequiresPermission constructor annotation should ensure the permission is present.
|
// @RequiresPermission constructor annotation should ensure the permission is present.
|
||||||
@SuppressWarnings("MissingPermission")
|
@SuppressWarnings("MissingPermission")
|
||||||
private static JobInfo buildJobInfo(
|
private static JobInfo buildJobInfo(
|
||||||
@ -94,8 +105,15 @@ public final class PlatformScheduler implements Scheduler {
|
|||||||
Requirements requirements,
|
Requirements requirements,
|
||||||
String serviceAction,
|
String serviceAction,
|
||||||
String servicePackage) {
|
String servicePackage) {
|
||||||
JobInfo.Builder builder = new JobInfo.Builder(jobId, jobServiceComponentName);
|
Requirements filteredRequirements = requirements.filterRequirements(SUPPORTED_REQUIREMENTS);
|
||||||
|
if (!filteredRequirements.equals(requirements)) {
|
||||||
|
Log.w(
|
||||||
|
TAG,
|
||||||
|
"Ignoring unsupported requirements: "
|
||||||
|
+ (filteredRequirements.getRequirements() ^ requirements.getRequirements()));
|
||||||
|
}
|
||||||
|
|
||||||
|
JobInfo.Builder builder = new JobInfo.Builder(jobId, jobServiceComponentName);
|
||||||
if (requirements.isUnmeteredNetworkRequired()) {
|
if (requirements.isUnmeteredNetworkRequired()) {
|
||||||
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
|
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
|
||||||
} else if (requirements.isNetworkRequired()) {
|
} else if (requirements.isNetworkRequired()) {
|
||||||
@ -103,6 +121,9 @@ public final class PlatformScheduler implements Scheduler {
|
|||||||
}
|
}
|
||||||
builder.setRequiresDeviceIdle(requirements.isIdleRequired());
|
builder.setRequiresDeviceIdle(requirements.isIdleRequired());
|
||||||
builder.setRequiresCharging(requirements.isChargingRequired());
|
builder.setRequiresCharging(requirements.isChargingRequired());
|
||||||
|
if (Util.SDK_INT >= 26 && requirements.isStorageNotLowRequired()) {
|
||||||
|
builder.setRequiresStorageNotLow(true);
|
||||||
|
}
|
||||||
builder.setPersisted(true);
|
builder.setPersisted(true);
|
||||||
|
|
||||||
PersistableBundle extras = new PersistableBundle();
|
PersistableBundle extras = new PersistableBundle();
|
||||||
|
@ -39,13 +39,13 @@ public final class Requirements implements Parcelable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Requirement flags. Possible flag values are {@link #NETWORK}, {@link #NETWORK_UNMETERED},
|
* Requirement flags. Possible flag values are {@link #NETWORK}, {@link #NETWORK_UNMETERED},
|
||||||
* {@link #DEVICE_IDLE} and {@link #DEVICE_CHARGING}.
|
* {@link #DEVICE_IDLE}, {@link #DEVICE_CHARGING} and {@link #DEVICE_STORAGE_NOT_LOW}.
|
||||||
*/
|
*/
|
||||||
@Documented
|
@Documented
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@IntDef(
|
@IntDef(
|
||||||
flag = true,
|
flag = true,
|
||||||
value = {NETWORK, NETWORK_UNMETERED, DEVICE_IDLE, DEVICE_CHARGING})
|
value = {NETWORK, NETWORK_UNMETERED, DEVICE_IDLE, DEVICE_CHARGING, DEVICE_STORAGE_NOT_LOW})
|
||||||
public @interface RequirementFlags {}
|
public @interface RequirementFlags {}
|
||||||
|
|
||||||
/** Requirement that the device has network connectivity. */
|
/** Requirement that the device has network connectivity. */
|
||||||
@ -56,6 +56,11 @@ public final class Requirements implements Parcelable {
|
|||||||
public static final int DEVICE_IDLE = 1 << 2;
|
public static final int DEVICE_IDLE = 1 << 2;
|
||||||
/** Requirement that the device is charging. */
|
/** Requirement that the device is charging. */
|
||||||
public static final int DEVICE_CHARGING = 1 << 3;
|
public static final int DEVICE_CHARGING = 1 << 3;
|
||||||
|
/**
|
||||||
|
* Requirement that the device's <em>internal</em> storage is not low. Note that this requirement
|
||||||
|
* is not affected by the status of external storage.
|
||||||
|
*/
|
||||||
|
public static final int DEVICE_STORAGE_NOT_LOW = 1 << 4;
|
||||||
|
|
||||||
@RequirementFlags private final int requirements;
|
@RequirementFlags private final int requirements;
|
||||||
|
|
||||||
@ -74,6 +79,18 @@ public final class Requirements implements Parcelable {
|
|||||||
return requirements;
|
return requirements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the requirements, returning the subset that are enabled by the provided filter.
|
||||||
|
*
|
||||||
|
* @param requirementsFilter The enabled {@link RequirementFlags}.
|
||||||
|
* @return The filtered requirements. If the filter does not cause a change in the requirements
|
||||||
|
* then this instance will be returned.
|
||||||
|
*/
|
||||||
|
public Requirements filterRequirements(int requirementsFilter) {
|
||||||
|
int filteredRequirements = requirements & requirementsFilter;
|
||||||
|
return filteredRequirements == requirements ? this : new Requirements(filteredRequirements);
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns whether network connectivity is required. */
|
/** Returns whether network connectivity is required. */
|
||||||
public boolean isNetworkRequired() {
|
public boolean isNetworkRequired() {
|
||||||
return (requirements & NETWORK) != 0;
|
return (requirements & NETWORK) != 0;
|
||||||
@ -94,6 +111,11 @@ public final class Requirements implements Parcelable {
|
|||||||
return (requirements & DEVICE_IDLE) != 0;
|
return (requirements & DEVICE_IDLE) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns whether the device is required to not be low on <em>internal</em> storage. */
|
||||||
|
public boolean isStorageNotLowRequired() {
|
||||||
|
return (requirements & DEVICE_STORAGE_NOT_LOW) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the requirements are met.
|
* Returns whether the requirements are met.
|
||||||
*
|
*
|
||||||
@ -119,6 +141,9 @@ public final class Requirements implements Parcelable {
|
|||||||
if (isIdleRequired() && !isDeviceIdle(context)) {
|
if (isIdleRequired() && !isDeviceIdle(context)) {
|
||||||
notMetRequirements |= DEVICE_IDLE;
|
notMetRequirements |= DEVICE_IDLE;
|
||||||
}
|
}
|
||||||
|
if (isStorageNotLowRequired() && !isStorageNotLow(context)) {
|
||||||
|
notMetRequirements |= DEVICE_STORAGE_NOT_LOW;
|
||||||
|
}
|
||||||
return notMetRequirements;
|
return notMetRequirements;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,8 +170,10 @@ public final class Requirements implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDeviceCharging(Context context) {
|
private boolean isDeviceCharging(Context context) {
|
||||||
|
@Nullable
|
||||||
Intent batteryStatus =
|
Intent batteryStatus =
|
||||||
context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
context.registerReceiver(
|
||||||
|
/* receiver= */ null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||||
if (batteryStatus == null) {
|
if (batteryStatus == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -162,6 +189,12 @@ public final class Requirements implements Parcelable {
|
|||||||
: Util.SDK_INT >= 20 ? !powerManager.isInteractive() : !powerManager.isScreenOn();
|
: Util.SDK_INT >= 20 ? !powerManager.isInteractive() : !powerManager.isScreenOn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isStorageNotLow(Context context) {
|
||||||
|
return context.registerReceiver(
|
||||||
|
/* receiver= */ null, new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW))
|
||||||
|
== null;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isInternetConnectivityValidated(ConnectivityManager connectivityManager) {
|
private static boolean isInternetConnectivityValidated(ConnectivityManager connectivityManager) {
|
||||||
// It's possible to query NetworkCapabilities from API level 23, but RequirementsWatcher only
|
// It's possible to query NetworkCapabilities from API level 23, but RequirementsWatcher only
|
||||||
// fires an event to update its Requirements when NetworkCapabilities change from API level 24.
|
// fires an event to update its Requirements when NetworkCapabilities change from API level 24.
|
||||||
|
@ -104,6 +104,10 @@ public final class RequirementsWatcher {
|
|||||||
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (requirements.isStorageNotLowRequired()) {
|
||||||
|
filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
|
||||||
|
filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
|
||||||
|
}
|
||||||
receiver = new DeviceStatusChangeReceiver();
|
receiver = new DeviceStatusChangeReceiver();
|
||||||
context.registerReceiver(receiver, filter, null, handler);
|
context.registerReceiver(receiver, filter, null, handler);
|
||||||
return notMetRequirements;
|
return notMetRequirements;
|
||||||
|
@ -45,4 +45,14 @@ public interface Scheduler {
|
|||||||
* @return Whether cancellation was successful.
|
* @return Whether cancellation was successful.
|
||||||
*/
|
*/
|
||||||
boolean cancel();
|
boolean cancel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this {@link Scheduler} supports the provided {@link Requirements}. If all of the
|
||||||
|
* requirements are supported then the same {@link Requirements} instance is returned. If not then
|
||||||
|
* a new instance is returned containing the subset of the requirements that are supported.
|
||||||
|
*
|
||||||
|
* @param requirements The requirements to check.
|
||||||
|
* @return The supported requirements.
|
||||||
|
*/
|
||||||
|
Requirements getSupportedRequirements(Requirements requirements);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user