summaryrefslogtreecommitdiff
path: root/com/android/server/job/controllers
diff options
context:
space:
mode:
authorJeff Davidson <jpd@google.com>2018-02-08 15:30:06 -0800
committerJeff Davidson <jpd@google.com>2018-02-08 15:30:06 -0800
commita192cc2a132cb0ee8588e2df755563ec7008c179 (patch)
tree380e4db22df19c819bd37df34bf06e7568916a50 /com/android/server/job/controllers
parent98fe7819c6d14f4f464a5cac047f9e82dee5da58 (diff)
downloadandroid-28-a192cc2a132cb0ee8588e2df755563ec7008c179.tar.gz
Update fullsdk to 4575844
/google/data/ro/projects/android/fetch_artifact \ --bid 4575844 \ --target sdk_phone_x86_64-sdk \ sdk-repo-linux-sources-4575844.zip Test: TreeHugger Change-Id: I81e0eb157b4ac3b38408d0ef86f9d6286471f87a
Diffstat (limited to 'com/android/server/job/controllers')
-rw-r--r--com/android/server/job/controllers/AppIdleController.java34
-rw-r--r--com/android/server/job/controllers/BackgroundJobsController.java76
-rw-r--r--com/android/server/job/controllers/BatteryController.java33
-rw-r--r--com/android/server/job/controllers/ConnectivityController.java151
-rw-r--r--com/android/server/job/controllers/ContentObserverController.java102
-rw-r--r--com/android/server/job/controllers/DeviceIdleJobsController.java37
-rw-r--r--com/android/server/job/controllers/IdleController.java25
-rw-r--r--com/android/server/job/controllers/JobStatus.java358
-rw-r--r--com/android/server/job/controllers/StateController.java3
-rw-r--r--com/android/server/job/controllers/StorageController.java36
-rw-r--r--com/android/server/job/controllers/TimeController.java42
11 files changed, 831 insertions, 66 deletions
diff --git a/com/android/server/job/controllers/AppIdleController.java b/com/android/server/job/controllers/AppIdleController.java
index a7ed2f56..8d11d1ee 100644
--- a/com/android/server/job/controllers/AppIdleController.java
+++ b/com/android/server/job/controllers/AppIdleController.java
@@ -20,10 +20,12 @@ import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.os.UserHandle;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobStore;
+import com.android.server.job.StateControllerProto;
import java.io.PrintWriter;
@@ -152,6 +154,38 @@ public final class AppIdleController extends StateController {
});
}
+ @Override
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+ final long token = proto.start(fieldId);
+ final long mToken = proto.start(StateControllerProto.APP_IDLE);
+
+ proto.write(StateControllerProto.AppIdleController.IS_PAROLE_ON, mAppIdleParoleOn);
+
+ mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
+ @Override public void process(JobStatus js) {
+ // Skip printing details if the caller requested a filter
+ if (!js.shouldDump(filterUid)) {
+ return;
+ }
+
+ final long jsToken =
+ proto.start(StateControllerProto.AppIdleController.TRACKED_JOBS);
+ js.writeToShortProto(proto, StateControllerProto.AppIdleController.TrackedJob.INFO);
+ proto.write(StateControllerProto.AppIdleController.TrackedJob.SOURCE_UID,
+ js.getSourceUid());
+ proto.write(StateControllerProto.AppIdleController.TrackedJob.SOURCE_PACKAGE_NAME,
+ js.getSourcePackageName());
+ proto.write(
+ StateControllerProto.AppIdleController.TrackedJob.ARE_CONSTRAINTS_SATISFIED,
+ (js.satisfiedConstraints & JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0);
+ proto.end(jsToken);
+ }
+ });
+
+ proto.end(mToken);
+ proto.end(token);
+ }
+
void setAppIdleParoleOn(boolean isAppIdleParoleOn) {
// Flag if any app's idle state has changed
boolean changed = false;
diff --git a/com/android/server/job/controllers/BackgroundJobsController.java b/com/android/server/job/controllers/BackgroundJobsController.java
index fc4015d0..2e4567ac 100644
--- a/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/com/android/server/job/controllers/BackgroundJobsController.java
@@ -17,17 +17,17 @@
package com.android.server.job.controllers;
import android.content.Context;
-import android.os.IDeviceIdleController;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
-import com.android.internal.util.ArrayUtils;
import com.android.server.ForceAppStandbyTracker;
import com.android.server.ForceAppStandbyTracker.Listener;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobStore;
+import com.android.server.job.StateControllerProto;
+import com.android.server.job.StateControllerProto.BackgroundJobsController.TrackedJob;
import java.io.PrintWriter;
@@ -41,7 +41,6 @@ public final class BackgroundJobsController extends StateController {
private static volatile BackgroundJobsController sController;
private final JobSchedulerService mJobSchedulerService;
- private final IDeviceIdleController mDeviceIdleController;
private final ForceAppStandbyTracker mForceAppStandbyTracker;
@@ -59,8 +58,6 @@ public final class BackgroundJobsController extends StateController {
private BackgroundJobsController(JobSchedulerService service, Context context, Object lock) {
super(service, context, lock);
mJobSchedulerService = service;
- mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
- ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
@@ -90,6 +87,7 @@ public final class BackgroundJobsController extends StateController {
return;
}
final int uid = jobStatus.getSourceUid();
+ final String sourcePkg = jobStatus.getSourcePackageName();
pw.print(" #");
jobStatus.printUniqueId(pw);
pw.print(" from ");
@@ -100,11 +98,10 @@ public final class BackgroundJobsController extends StateController {
pw.print(", whitelisted");
}
pw.print(": ");
- pw.print(jobStatus.getSourcePackageName());
+ pw.print(sourcePkg);
pw.print(" [RUN_ANY_IN_BACKGROUND ");
- pw.print(mForceAppStandbyTracker.isRunAnyInBackgroundAppOpsAllowed(
- jobStatus.getSourceUid(), jobStatus.getSourcePackageName())
+ pw.print(mForceAppStandbyTracker.isRunAnyInBackgroundAppOpsAllowed(uid, sourcePkg)
? "allowed]" : "disallowed]");
if ((jobStatus.satisfiedConstraints
@@ -116,6 +113,51 @@ public final class BackgroundJobsController extends StateController {
});
}
+ @Override
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+ final long token = proto.start(fieldId);
+ final long mToken = proto.start(StateControllerProto.BACKGROUND);
+
+ mForceAppStandbyTracker.dumpProto(proto,
+ StateControllerProto.BackgroundJobsController.FORCE_APP_STANDBY_TRACKER);
+
+ mJobSchedulerService.getJobStore().forEachJob((jobStatus) -> {
+ if (!jobStatus.shouldDump(filterUid)) {
+ return;
+ }
+ final long jsToken =
+ proto.start(StateControllerProto.BackgroundJobsController.TRACKED_JOBS);
+
+ jobStatus.writeToShortProto(proto,
+ TrackedJob.INFO);
+ final int sourceUid = jobStatus.getSourceUid();
+ proto.write(TrackedJob.SOURCE_UID, sourceUid);
+ final String sourcePkg = jobStatus.getSourcePackageName();
+ proto.write(TrackedJob.SOURCE_PACKAGE_NAME, sourcePkg);
+
+ proto.write(TrackedJob.IS_IN_FOREGROUND,
+ mForceAppStandbyTracker.isInForeground(sourceUid));
+ proto.write(TrackedJob.IS_WHITELISTED,
+ mForceAppStandbyTracker.isUidPowerSaveWhitelisted(sourceUid) ||
+ mForceAppStandbyTracker.isUidTempPowerSaveWhitelisted(sourceUid));
+
+ proto.write(
+ TrackedJob.CAN_RUN_ANY_IN_BACKGROUND,
+ mForceAppStandbyTracker.isRunAnyInBackgroundAppOpsAllowed(
+ sourceUid, sourcePkg));
+
+ proto.write(
+ TrackedJob.ARE_CONSTRAINTS_SATISFIED,
+ (jobStatus.satisfiedConstraints &
+ JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0);
+
+ proto.end(jsToken);
+ });
+
+ proto.end(mToken);
+ proto.end(token);
+ }
+
private void updateAllJobRestrictionsLocked() {
updateJobRestrictionsLocked(/*filterUid=*/ -1);
}
@@ -155,7 +197,9 @@ public final class BackgroundJobsController extends StateController {
final int uid = jobStatus.getSourceUid();
final String packageName = jobStatus.getSourcePackageName();
- final boolean canRun = !mForceAppStandbyTracker.areJobsRestricted(uid, packageName);
+ final boolean canRun = !mForceAppStandbyTracker.areJobsRestricted(uid, packageName,
+ (jobStatus.getInternalFlags() & JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION)
+ != 0);
return jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
}
@@ -187,17 +231,23 @@ public final class BackgroundJobsController extends StateController {
private final Listener mForceAppStandbyListener = new Listener() {
@Override
public void updateAllJobs() {
- updateAllJobRestrictionsLocked();
+ synchronized (mLock) {
+ updateAllJobRestrictionsLocked();
+ }
}
@Override
public void updateJobsForUid(int uid) {
- updateJobRestrictionsForUidLocked(uid);
+ synchronized (mLock) {
+ updateJobRestrictionsForUidLocked(uid);
+ }
}
@Override
public void updateJobsForUidPackage(int uid, String packageName) {
- updateJobRestrictionsForUidLocked(uid);
+ synchronized (mLock) {
+ updateJobRestrictionsForUidLocked(uid);
+ }
}
};
}
diff --git a/com/android/server/job/controllers/BatteryController.java b/com/android/server/job/controllers/BatteryController.java
index 76ff8348..8d3d116e 100644
--- a/com/android/server/job/controllers/BatteryController.java
+++ b/com/android/server/job/controllers/BatteryController.java
@@ -27,11 +27,13 @@ import android.os.BatteryManagerInternal;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateChangedListener;
+import com.android.server.job.StateControllerProto;
import java.io.PrintWriter;
@@ -263,4 +265,35 @@ public final class BatteryController extends StateController {
pw.println();
}
}
+
+ @Override
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+ final long token = proto.start(fieldId);
+ final long mToken = proto.start(StateControllerProto.BATTERY);
+
+ proto.write(StateControllerProto.BatteryController.IS_ON_STABLE_POWER,
+ mChargeTracker.isOnStablePower());
+ proto.write(StateControllerProto.BatteryController.IS_BATTERY_NOT_LOW,
+ mChargeTracker.isBatteryNotLow());
+
+ proto.write(StateControllerProto.BatteryController.IS_MONITORING,
+ mChargeTracker.isMonitoring());
+ proto.write(StateControllerProto.BatteryController.LAST_BROADCAST_SEQUENCE_NUMBER,
+ mChargeTracker.getSeq());
+
+ for (int i = 0; i < mTrackedTasks.size(); i++) {
+ final JobStatus js = mTrackedTasks.valueAt(i);
+ if (!js.shouldDump(filterUid)) {
+ continue;
+ }
+ final long jsToken = proto.start(StateControllerProto.BatteryController.TRACKED_JOBS);
+ js.writeToShortProto(proto, StateControllerProto.BatteryController.TrackedJob.INFO);
+ proto.write(StateControllerProto.BatteryController.TrackedJob.SOURCE_UID,
+ js.getSourceUid());
+ proto.end(jsToken);
+ }
+
+ proto.end(mToken);
+ proto.end(token);
+ }
}
diff --git a/com/android/server/job/controllers/ConnectivityController.java b/com/android/server/job/controllers/ConnectivityController.java
index da287691..373d87d9 100644
--- a/com/android/server/job/controllers/ConnectivityController.java
+++ b/com/android/server/job/controllers/ConnectivityController.java
@@ -16,6 +16,10 @@
package com.android.server.job.controllers;
+import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+
import android.app.job.JobInfo;
import android.content.Context;
import android.net.ConnectivityManager;
@@ -25,17 +29,21 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkPolicyManager;
+import android.net.NetworkRequest;
import android.net.TrafficStats;
import android.os.Process;
import android.os.UserHandle;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobServiceContext;
import com.android.server.job.StateChangedListener;
+import com.android.server.job.StateControllerProto;
import java.io.PrintWriter;
@@ -59,15 +67,15 @@ public final class ConnectivityController extends StateController implements
private final ArraySet<JobStatus> mTrackedJobs = new ArraySet<>();
/** Singleton. */
- private static ConnectivityController mSingleton;
+ private static ConnectivityController sSingleton;
private static Object sCreationLock = new Object();
public static ConnectivityController get(JobSchedulerService jms) {
synchronized (sCreationLock) {
- if (mSingleton == null) {
- mSingleton = new ConnectivityController(jms, jms.getContext(), jms.getLock());
+ if (sSingleton == null) {
+ sSingleton = new ConnectivityController(jms, jms.getContext(), jms.getLock());
}
- return mSingleton;
+ return sSingleton;
}
}
@@ -102,37 +110,29 @@ public final class ConnectivityController extends StateController implements
}
/**
- * Test to see if running the given job on the given network is sane.
+ * Test to see if running the given job on the given network is insane.
* <p>
* For example, if a job is trying to send 10MB over a 128Kbps EDGE
* connection, it would take 10.4 minutes, and has no chance of succeeding
* before the job times out, so we'd be insane to try running it.
*/
- private boolean isSane(JobStatus jobStatus, NetworkCapabilities capabilities) {
+ @SuppressWarnings("unused")
+ private static boolean isInsane(JobStatus jobStatus, Network network,
+ NetworkCapabilities capabilities) {
final long estimatedBytes = jobStatus.getEstimatedNetworkBytes();
if (estimatedBytes == JobInfo.NETWORK_BYTES_UNKNOWN) {
// We don't know how large the job is; cross our fingers!
- return true;
- }
- if (capabilities == null) {
- // We don't know what the network is like; cross our fingers!
- return true;
+ return false;
}
// We don't ask developers to differentiate between upstream/downstream
// in their size estimates, so test against the slowest link direction.
- final long downstream = capabilities.getLinkDownstreamBandwidthKbps();
- final long upstream = capabilities.getLinkUpstreamBandwidthKbps();
- final long slowest;
- if (downstream > 0 && upstream > 0) {
- slowest = Math.min(downstream, upstream);
- } else if (downstream > 0) {
- slowest = downstream;
- } else if (upstream > 0) {
- slowest = upstream;
- } else {
+ final long slowest = NetworkCapabilities.minBandwidth(
+ capabilities.getLinkDownstreamBandwidthKbps(),
+ capabilities.getLinkUpstreamBandwidthKbps());
+ if (slowest == LINK_BANDWIDTH_UNSPECIFIED) {
// We don't know what the network is like; cross our fingers!
- return true;
+ return false;
}
final long estimatedMillis = ((estimatedBytes * DateUtils.SECOND_IN_MILLIS)
@@ -141,28 +141,87 @@ public final class ConnectivityController extends StateController implements
// If we'd never finish before the timeout, we'd be insane!
Slog.w(TAG, "Estimated " + estimatedBytes + " bytes over " + slowest
+ " kbps network would take " + estimatedMillis + "ms; that's insane!");
+ return true;
+ } else {
return false;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static boolean isCongestionDelayed(JobStatus jobStatus, Network network,
+ NetworkCapabilities capabilities) {
+ // If network is congested, and job is less than 50% through the
+ // developer-requested window, then we're okay delaying the job.
+ if (!capabilities.hasCapability(NET_CAPABILITY_NOT_CONGESTED)) {
+ return jobStatus.getFractionRunTime() < 0.5;
} else {
- return true;
+ return false;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static boolean isStrictSatisfied(JobStatus jobStatus, Network network,
+ NetworkCapabilities capabilities) {
+ return jobStatus.getJob().getRequiredNetwork().networkCapabilities
+ .satisfiedByNetworkCapabilities(capabilities);
+ }
+
+ @SuppressWarnings("unused")
+ private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
+ NetworkCapabilities capabilities) {
+ // Only consider doing this for prefetching jobs
+ if ((jobStatus.getJob().getFlags() & JobInfo.FLAG_IS_PREFETCH) == 0) {
+ return false;
+ }
+
+ // See if we match after relaxing any unmetered request
+ final NetworkCapabilities relaxed = new NetworkCapabilities(
+ jobStatus.getJob().getRequiredNetwork().networkCapabilities)
+ .removeCapability(NET_CAPABILITY_NOT_METERED);
+ if (relaxed.satisfiedByNetworkCapabilities(capabilities)) {
+ // TODO: treat this as "maybe" response; need to check quotas
+ return jobStatus.getFractionRunTime() > 0.5;
+ } else {
+ return false;
}
}
+ @VisibleForTesting
+ static boolean isSatisfied(JobStatus jobStatus, Network network,
+ NetworkCapabilities capabilities) {
+ // Zeroth, we gotta have a network to think about being satisfied
+ if (network == null || capabilities == null) return false;
+
+ // First, are we insane?
+ if (isInsane(jobStatus, network, capabilities)) return false;
+
+ // Second, is the network congested?
+ if (isCongestionDelayed(jobStatus, network, capabilities)) return false;
+
+ // Third, is the network a strict match?
+ if (isStrictSatisfied(jobStatus, network, capabilities)) return true;
+
+ // Third, is the network a relaxed match?
+ if (isRelaxedSatisfied(jobStatus, network, capabilities)) return true;
+
+ return false;
+ }
+
private boolean updateConstraintsSatisfied(JobStatus jobStatus) {
// TODO: consider matching against non-active networks
final int jobUid = jobStatus.getSourceUid();
final boolean ignoreBlocked = (jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
+
final Network network = mConnManager.getActiveNetworkForUid(jobUid, ignoreBlocked);
final NetworkInfo info = mConnManager.getNetworkInfoForUid(network, jobUid, ignoreBlocked);
final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network);
final boolean connected = (info != null) && info.isConnected();
- final boolean satisfied = jobStatus.getJob().getRequiredNetwork().networkCapabilities
- .satisfiedByNetworkCapabilities(capabilities);
- final boolean sane = isSane(jobStatus, capabilities);
+ final boolean satisfied = isSatisfied(jobStatus, network, capabilities);
final boolean changed = jobStatus
- .setConnectivityConstraintSatisfied(connected && satisfied && sane);
+ .setConnectivityConstraintSatisfied(connected && satisfied);
// Pass along the evaluated network for job to use; prevents race
// conditions as default routes change over time, and opens the door to
@@ -178,8 +237,7 @@ public final class ConnectivityController extends StateController implements
if (DEBUG) {
Slog.i(TAG, "Connectivity " + (changed ? "CHANGED" : "unchanged")
+ " for " + jobStatus + ": connected=" + connected
- + " satisfied=" + satisfied
- + " sane=" + sane);
+ + " satisfied=" + satisfied);
}
return changed;
}
@@ -241,7 +299,7 @@ public final class ConnectivityController extends StateController implements
}
};
- private final INetworkPolicyListener mNetPolicyListener = new INetworkPolicyListener.Stub() {
+ private final INetworkPolicyListener mNetPolicyListener = new NetworkPolicyManager.Listener() {
@Override
public void onUidRulesChanged(int uid, int uidRules) {
if (DEBUG) {
@@ -251,11 +309,6 @@ public final class ConnectivityController extends StateController implements
}
@Override
- public void onMeteredIfacesChanged(String[] meteredIfaces) {
- // We track this via our NetworkCallback
- }
-
- @Override
public void onRestrictBackgroundChanged(boolean restrictBackground) {
if (DEBUG) {
Slog.v(TAG, "Background restriction change to " + restrictBackground);
@@ -290,4 +343,32 @@ public final class ConnectivityController extends StateController implements
}
}
}
+
+ @Override
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+ final long token = proto.start(fieldId);
+ final long mToken = proto.start(StateControllerProto.CONNECTIVITY);
+
+ proto.write(StateControllerProto.ConnectivityController.IS_CONNECTED, mConnected);
+
+ for (int i = 0; i < mTrackedJobs.size(); i++) {
+ final JobStatus js = mTrackedJobs.valueAt(i);
+ if (!js.shouldDump(filterUid)) {
+ continue;
+ }
+ final long jsToken = proto.start(StateControllerProto.ConnectivityController.TRACKED_JOBS);
+ js.writeToShortProto(proto, StateControllerProto.ConnectivityController.TrackedJob.INFO);
+ proto.write(StateControllerProto.ConnectivityController.TrackedJob.SOURCE_UID,
+ js.getSourceUid());
+ NetworkRequest rn = js.getJob().getRequiredNetwork();
+ if (rn != null) {
+ rn.writeToProto(proto,
+ StateControllerProto.ConnectivityController.TrackedJob.REQUIRED_NETWORK);
+ }
+ proto.end(jsToken);
+ }
+
+ proto.end(mToken);
+ proto.end(token);
+ }
}
diff --git a/com/android/server/job/controllers/ContentObserverController.java b/com/android/server/job/controllers/ContentObserverController.java
index ff807ecc..7394e23f 100644
--- a/com/android/server/job/controllers/ContentObserverController.java
+++ b/com/android/server/job/controllers/ContentObserverController.java
@@ -28,10 +28,13 @@ import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateChangedListener;
+import com.android.server.job.StateControllerProto;
+import com.android.server.job.StateControllerProto.ContentObserverController.Observer.TriggerContentData;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -451,4 +454,103 @@ public final class ContentObserverController extends StateController {
}
}
}
+
+ @Override
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+ final long token = proto.start(fieldId);
+ final long mToken = proto.start(StateControllerProto.CONTENT_OBSERVER);
+
+ for (int i = 0; i < mTrackedTasks.size(); i++) {
+ JobStatus js = mTrackedTasks.valueAt(i);
+ if (!js.shouldDump(filterUid)) {
+ continue;
+ }
+ final long jsToken =
+ proto.start(StateControllerProto.ContentObserverController.TRACKED_JOBS);
+ js.writeToShortProto(proto,
+ StateControllerProto.ContentObserverController.TrackedJob.INFO);
+ proto.write(StateControllerProto.ContentObserverController.TrackedJob.SOURCE_UID,
+ js.getSourceUid());
+ proto.end(jsToken);
+ }
+
+ final int n = mObservers.size();
+ for (int userIdx = 0; userIdx < n; userIdx++) {
+ final long oToken =
+ proto.start(StateControllerProto.ContentObserverController.OBSERVERS);
+ final int userId = mObservers.keyAt(userIdx);
+
+ proto.write(StateControllerProto.ContentObserverController.Observer.USER_ID, userId);
+
+ ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observersOfUser =
+ mObservers.get(userId);
+ int numbOfObserversPerUser = observersOfUser.size();
+ for (int observerIdx = 0 ; observerIdx < numbOfObserversPerUser; observerIdx++) {
+ ObserverInstance obs = observersOfUser.valueAt(observerIdx);
+ int m = obs.mJobs.size();
+ boolean shouldDump = false;
+ for (int j = 0; j < m; j++) {
+ JobInstance inst = obs.mJobs.valueAt(j);
+ if (inst.mJobStatus.shouldDump(filterUid)) {
+ shouldDump = true;
+ break;
+ }
+ }
+ if (!shouldDump) {
+ continue;
+ }
+ final long tToken = proto.start(
+ StateControllerProto.ContentObserverController.Observer.TRIGGERS);
+
+ JobInfo.TriggerContentUri trigger = observersOfUser.keyAt(observerIdx);
+ Uri u = trigger.getUri();
+ if (u != null) {
+ proto.write(TriggerContentData.URI, u.toString());
+ }
+ proto.write(TriggerContentData.FLAGS, trigger.getFlags());
+
+ for (int j = 0; j < m; j++) {
+ final long jToken = proto.start(TriggerContentData.JOBS);
+ JobInstance inst = obs.mJobs.valueAt(j);
+
+ inst.mJobStatus.writeToShortProto(proto, TriggerContentData.JobInstance.INFO);
+ proto.write(TriggerContentData.JobInstance.SOURCE_UID,
+ inst.mJobStatus.getSourceUid());
+
+ if (inst.mChangedAuthorities == null) {
+ proto.end(jToken);
+ continue;
+ }
+ if (inst.mTriggerPending) {
+ proto.write(TriggerContentData.JobInstance.TRIGGER_CONTENT_UPDATE_DELAY_MS,
+ inst.mJobStatus.getTriggerContentUpdateDelay());
+ proto.write(TriggerContentData.JobInstance.TRIGGER_CONTENT_MAX_DELAY_MS,
+ inst.mJobStatus.getTriggerContentMaxDelay());
+ }
+ for (int k = 0; k < inst.mChangedAuthorities.size(); k++) {
+ proto.write(TriggerContentData.JobInstance.CHANGED_AUTHORITIES,
+ inst.mChangedAuthorities.valueAt(k));
+ }
+ if (inst.mChangedUris != null) {
+ for (int k = 0; k < inst.mChangedUris.size(); k++) {
+ u = inst.mChangedUris.valueAt(k);
+ if (u != null) {
+ proto.write(TriggerContentData.JobInstance.CHANGED_URIS,
+ u.toString());
+ }
+ }
+ }
+
+ proto.end(jToken);
+ }
+
+ proto.end(tToken);
+ }
+
+ proto.end(oToken);
+ }
+
+ proto.end(mToken);
+ proto.end(token);
+ }
}
diff --git a/com/android/server/job/controllers/DeviceIdleJobsController.java b/com/android/server/job/controllers/DeviceIdleJobsController.java
index b7eb9e06..0dbcbeeb 100644
--- a/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -29,12 +29,15 @@ import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseBooleanArray;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.ArrayUtils;
import com.android.server.DeviceIdleController;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobStore;
+import com.android.server.job.StateControllerProto;
+import com.android.server.job.StateControllerProto.DeviceIdleJobsController.TrackedJob;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -270,6 +273,38 @@ public final class DeviceIdleJobsController extends StateController {
});
}
+ @Override
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+ final long token = proto.start(fieldId);
+ final long mToken = proto.start(StateControllerProto.DEVICE_IDLE);
+
+ proto.write(StateControllerProto.DeviceIdleJobsController.IS_DEVICE_IDLE_MODE,
+ mDeviceIdleMode);
+ mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
+ @Override public void process(JobStatus jobStatus) {
+ if (!jobStatus.shouldDump(filterUid)) {
+ return;
+ }
+ final long jsToken =
+ proto.start(StateControllerProto.DeviceIdleJobsController.TRACKED_JOBS);
+
+ jobStatus.writeToShortProto(proto, TrackedJob.INFO);
+ proto.write(TrackedJob.SOURCE_UID, jobStatus.getSourceUid());
+ proto.write(TrackedJob.SOURCE_PACKAGE_NAME, jobStatus.getSourcePackageName());
+ proto.write(TrackedJob.ARE_CONSTRAINTS_SATISFIED,
+ (jobStatus.satisfiedConstraints &
+ JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0);
+ proto.write(TrackedJob.IS_DOZE_WHITELISTED, jobStatus.dozeWhitelisted);
+ proto.write(TrackedJob.IS_ALLOWED_IN_DOZE, mAllowInIdleJobs.contains(jobStatus));
+
+ proto.end(jsToken);
+ }
+ });
+
+ proto.end(mToken);
+ proto.end(token);
+ }
+
final class DeviceIdleUpdateFunctor implements JobStore.JobStatusFunctor {
boolean mChanged;
@@ -300,4 +335,4 @@ public final class DeviceIdleJobsController extends StateController {
}
}
}
-} \ No newline at end of file
+}
diff --git a/com/android/server/job/controllers/IdleController.java b/com/android/server/job/controllers/IdleController.java
index 7bde1744..a9bc7e0d 100644
--- a/com/android/server/job/controllers/IdleController.java
+++ b/com/android/server/job/controllers/IdleController.java
@@ -27,10 +27,12 @@ import android.content.IntentFilter;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.server.am.ActivityManagerService;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateChangedListener;
+import com.android.server.job.StateControllerProto;
import java.io.PrintWriter;
@@ -216,4 +218,27 @@ public final class IdleController extends StateController {
pw.println();
}
}
+
+ @Override
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+ final long token = proto.start(fieldId);
+ final long mToken = proto.start(StateControllerProto.IDLE);
+
+ proto.write(StateControllerProto.IdleController.IS_IDLE, mIdleTracker.isIdle());
+
+ for (int i = 0; i < mTrackedTasks.size(); i++) {
+ final JobStatus js = mTrackedTasks.valueAt(i);
+ if (!js.shouldDump(filterUid)) {
+ continue;
+ }
+ final long jsToken = proto.start(StateControllerProto.IdleController.TRACKED_JOBS);
+ js.writeToShortProto(proto, StateControllerProto.IdleController.TrackedJob.INFO);
+ proto.write(StateControllerProto.IdleController.TrackedJob.SOURCE_UID,
+ js.getSourceUid());
+ proto.end(jsToken);
+ }
+
+ proto.end(mToken);
+ proto.end(token);
+ }
}
diff --git a/com/android/server/job/controllers/JobStatus.java b/com/android/server/job/controllers/JobStatus.java
index e71b8ec4..d9a5ff67 100644
--- a/com/android/server/job/controllers/JobStatus.java
+++ b/com/android/server/job/controllers/JobStatus.java
@@ -24,6 +24,7 @@ import android.app.job.JobInfo;
import android.app.job.JobWorkItem;
import android.content.ClipData;
import android.content.ComponentName;
+import android.content.pm.PackageManagerInternal;
import android.net.Network;
import android.net.Uri;
import android.os.RemoteException;
@@ -33,15 +34,19 @@ import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import com.android.server.LocalServices;
import com.android.server.job.GrantedUriPermissions;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobStatusDumpProto;
+import com.android.server.job.JobStatusShortInfoProto;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.function.Predicate;
/**
* Uniquely identifies a job internally.
@@ -93,6 +98,7 @@ public final class JobStatus {
final JobInfo job;
/** Uid of the package requesting this job. */
final int callingUid;
+ final int targetSdkVersion;
final String batteryName;
final String sourcePackageName;
@@ -179,6 +185,21 @@ public final class JobStatus {
*/
private int trackingControllers;
+ /**
+ * Flag for {@link #mInternalFlags}: this job was scheduled when the app that owns the job
+ * service (not necessarily the caller) was in the foreground and the job has no time
+ * constraints, which makes it exempted from the battery saver job restriction.
+ *
+ * @hide
+ */
+ public static final int INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION = 1 << 0;
+
+ /**
+ * Versatile, persistable flags for a job that's updated within the system server,
+ * as opposed to {@link JobInfo#flags} that's set by callers.
+ */
+ private int mInternalFlags;
+
// These are filled in by controllers when preparing for execution.
public ArraySet<Uri> changedUris;
public ArraySet<String> changedAuthorities;
@@ -240,12 +261,13 @@ public final class JobStatus {
return callingUid;
}
- private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
+ private JobStatus(JobInfo job, int callingUid, int targetSdkVersion, String sourcePackageName,
int sourceUserId, int standbyBucket, long heartbeat, String tag, int numFailures,
long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
- long lastSuccessfulRunTime, long lastFailedRunTime) {
+ long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags) {
this.job = job;
this.callingUid = callingUid;
+ this.targetSdkVersion = targetSdkVersion;
this.standbyBucket = standbyBucket;
this.baseHeartbeat = heartbeat;
@@ -298,18 +320,21 @@ public final class JobStatus {
mLastSuccessfulRunTime = lastSuccessfulRunTime;
mLastFailedRunTime = lastFailedRunTime;
+ mInternalFlags = internalFlags;
+
updateEstimatedNetworkBytesLocked();
}
/** Copy constructor: used specifically when cloning JobStatus objects for persistence,
* so we preserve RTC window bounds if the source object has them. */
public JobStatus(JobStatus jobStatus) {
- this(jobStatus.getJob(), jobStatus.getUid(),
+ this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.targetSdkVersion,
jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
jobStatus.getStandbyBucket(), jobStatus.getBaseHeartbeat(),
jobStatus.getSourceTag(), jobStatus.getNumFailures(),
jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(),
- jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime());
+ jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(),
+ jobStatus.getInternalFlags());
mPersistedUtcTimes = jobStatus.mPersistedUtcTimes;
if (jobStatus.mPersistedUtcTimes != null) {
if (DEBUG) {
@@ -330,12 +355,13 @@ public final class JobStatus {
int standbyBucket, long baseHeartbeat, String sourceTag,
long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
long lastSuccessfulRunTime, long lastFailedRunTime,
- Pair<Long, Long> persistedExecutionTimesUTC) {
- this(job, callingUid, sourcePkgName, sourceUserId,
+ Pair<Long, Long> persistedExecutionTimesUTC,
+ int innerFlags) {
+ this(job, callingUid, resolveTargetSdkVersion(job), sourcePkgName, sourceUserId,
standbyBucket, baseHeartbeat,
sourceTag, 0,
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
- lastSuccessfulRunTime, lastFailedRunTime);
+ lastSuccessfulRunTime, lastFailedRunTime, innerFlags);
// Only during initial inflation do we record the UTC-timebase execution bounds
// read from the persistent store. If we ever have to recreate the JobStatus on
@@ -354,12 +380,12 @@ public final class JobStatus {
long newEarliestRuntimeElapsedMillis,
long newLatestRuntimeElapsedMillis, int backoffAttempt,
long lastSuccessfulRunTime, long lastFailedRunTime) {
- this(rescheduling.job, rescheduling.getUid(),
+ this(rescheduling.job, rescheduling.getUid(), resolveTargetSdkVersion(rescheduling.job),
rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(),
rescheduling.getStandbyBucket(), newBaseHeartbeat,
rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
newLatestRuntimeElapsedMillis,
- lastSuccessfulRunTime, lastFailedRunTime);
+ lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags());
}
/**
@@ -389,10 +415,12 @@ public final class JobStatus {
sourceUserId, elapsedNow);
JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
long currentHeartbeat = js != null ? js.currentHeartbeat() : 0;
- return new JobStatus(job, callingUid, sourcePkg, sourceUserId,
+
+ return new JobStatus(job, callingUid, resolveTargetSdkVersion(job), sourcePkg, sourceUserId,
standbyBucket, currentHeartbeat, tag, 0,
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
- 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */);
+ 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
+ /*innerFlags=*/ 0);
}
public void enqueueWorkLocked(IActivityManager am, JobWorkItem work) {
@@ -536,6 +564,10 @@ public final class JobStatus {
return job.getId();
}
+ public int getTargetSdkVersion() {
+ return targetSdkVersion;
+ }
+
public void printUniqueId(PrintWriter pw) {
UserHandle.formatUid(pw, callingUid);
pw.print("/");
@@ -613,6 +645,28 @@ public final class JobStatus {
return job.getFlags();
}
+ public int getInternalFlags() {
+ return mInternalFlags;
+ }
+
+ public void addInternalFlags(int flags) {
+ mInternalFlags |= flags;
+ }
+
+ public void maybeAddForegroundExemption(Predicate<Integer> uidForegroundChecker) {
+ // Jobs with time constraints shouldn't be exempted.
+ if (job.hasEarlyConstraint() || job.hasLateConstraint()) {
+ return;
+ }
+ // Already exempted, skip the foreground check.
+ if ((mInternalFlags & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) {
+ return;
+ }
+ if (uidForegroundChecker.test(getSourceUid())) {
+ addInternalFlags(INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION);
+ }
+ }
+
private void updateEstimatedNetworkBytesLocked() {
totalNetworkBytes = computeEstimatedNetworkBytesLocked();
}
@@ -710,6 +764,37 @@ public final class JobStatus {
return latestRunTimeElapsedMillis;
}
+ /**
+ * Return the fractional position of "now" within the "run time" window of
+ * this job.
+ * <p>
+ * For example, if the earliest run time was 10 minutes ago, and the latest
+ * run time is 30 minutes from now, this would return 0.25.
+ * <p>
+ * If the job has no window defined, returns 1. When only an earliest or
+ * latest time is defined, it's treated as an infinitely small window at
+ * that time.
+ */
+ public float getFractionRunTime() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ if (earliestRunTimeElapsedMillis == 0 && latestRunTimeElapsedMillis == Long.MAX_VALUE) {
+ return 1;
+ } else if (earliestRunTimeElapsedMillis == 0) {
+ return now >= latestRunTimeElapsedMillis ? 1 : 0;
+ } else if (latestRunTimeElapsedMillis == Long.MAX_VALUE) {
+ return now >= earliestRunTimeElapsedMillis ? 1 : 0;
+ } else {
+ if (now <= earliestRunTimeElapsedMillis) {
+ return 0;
+ } else if (now >= latestRunTimeElapsedMillis) {
+ return 1;
+ } else {
+ return (float) (now - earliestRunTimeElapsedMillis)
+ / (float) (latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis);
+ }
+ }
+ }
+
public Pair<Long, Long> getPersistedUtcTimes() {
return mPersistedUtcTimes;
}
@@ -968,6 +1053,20 @@ public final class JobStatus {
return sb.toString();
}
+ /**
+ * Convenience function to dump data that identifies a job uniquely to proto. This is intended
+ * to mimic {@link #toShortString}.
+ */
+ public void writeToShortProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ proto.write(JobStatusShortInfoProto.CALLING_UID, callingUid);
+ proto.write(JobStatusShortInfoProto.JOB_ID, job.getId());
+ proto.write(JobStatusShortInfoProto.BATTERY_NAME, batteryName);
+
+ proto.end(token);
+ }
+
void dumpConstraints(PrintWriter pw, int constraints) {
if ((constraints&CONSTRAINT_CHARGING) != 0) {
pw.print(" CHARGING");
@@ -999,6 +1098,48 @@ public final class JobStatus {
if ((constraints&CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
pw.print(" DEVICE_NOT_DOZING");
}
+ if ((constraints&CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
+ pw.print(" BACKGROUND_NOT_RESTRICTED");
+ }
+ if (constraints != 0) {
+ pw.print(" [0x");
+ pw.print(Integer.toHexString(constraints));
+ pw.print("]");
+ }
+ }
+
+ /** Writes constraints to the given repeating proto field. */
+ void dumpConstraints(ProtoOutputStream proto, long fieldId, int constraints) {
+ if ((constraints & CONSTRAINT_CHARGING) != 0) {
+ proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CHARGING);
+ }
+ if ((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0) {
+ proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_BATTERY_NOT_LOW);
+ }
+ if ((constraints & CONSTRAINT_STORAGE_NOT_LOW) != 0) {
+ proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_STORAGE_NOT_LOW);
+ }
+ if ((constraints & CONSTRAINT_TIMING_DELAY) != 0) {
+ proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_TIMING_DELAY);
+ }
+ if ((constraints & CONSTRAINT_DEADLINE) != 0) {
+ proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_DEADLINE);
+ }
+ if ((constraints & CONSTRAINT_IDLE) != 0) {
+ proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_IDLE);
+ }
+ if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) {
+ proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CONNECTIVITY);
+ }
+ if ((constraints & CONSTRAINT_APP_NOT_IDLE) != 0) {
+ proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_APP_NOT_IDLE);
+ }
+ if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) {
+ proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CONTENT_TRIGGER);
+ }
+ if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
+ proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_DEVICE_NOT_DOZING);
+ }
}
private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) {
@@ -1011,6 +1152,22 @@ public final class JobStatus {
}
}
+ private void dumpJobWorkItem(ProtoOutputStream proto, long fieldId, JobWorkItem work) {
+ final long token = proto.start(fieldId);
+
+ proto.write(JobStatusDumpProto.JobWorkItem.WORK_ID, work.getWorkId());
+ proto.write(JobStatusDumpProto.JobWorkItem.DELIVERY_COUNT, work.getDeliveryCount());
+ if (work.getIntent() != null) {
+ work.getIntent().writeToProto(proto, JobStatusDumpProto.JobWorkItem.INTENT);
+ }
+ Object grants = work.getGrants();
+ if (grants != null) {
+ ((GrantedUriPermissions) grants).dump(proto, JobStatusDumpProto.JobWorkItem.URI_GRANTS);
+ }
+
+ proto.end(token);
+ }
+
// normalized bucket indices, not the AppStandby constants
private String bucketName(int bucket) {
switch (bucket) {
@@ -1024,6 +1181,11 @@ public final class JobStatus {
}
}
+ private static int resolveTargetSdkVersion(JobInfo job) {
+ return LocalServices.getService(PackageManagerInternal.class)
+ .getPackageTargetSdkVersion(job.getService().getPackageName());
+ }
+
// Dumpsys infrastructure
public void dump(PrintWriter pw, String prefix, boolean full, long elapsedRealtimeMillis) {
pw.print(prefix); UserHandle.formatUid(pw, callingUid);
@@ -1052,6 +1214,15 @@ public final class JobStatus {
pw.print(prefix); pw.print(" Flags: ");
pw.println(Integer.toHexString(job.getFlags()));
}
+ if (getInternalFlags() != 0) {
+ pw.print(prefix); pw.print(" Internal flags: ");
+ pw.print(Integer.toHexString(getInternalFlags()));
+
+ if ((getInternalFlags()&INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) {
+ pw.print(" HAS_FOREGROUND_EXEMPTION");
+ }
+ pw.println();
+ }
pw.print(prefix); pw.print(" Requires: charging=");
pw.print(job.isRequireCharging()); pw.print(" batteryNotLow=");
pw.print(job.isRequireBatteryNotLow()); pw.print(" deviceIdle=");
@@ -1198,4 +1369,169 @@ public final class JobStatus {
pw.println(t.format(format));
}
}
+
+ public void dump(ProtoOutputStream proto, long fieldId, boolean full, long elapsedRealtimeMillis) {
+ final long token = proto.start(fieldId);
+
+ proto.write(JobStatusDumpProto.CALLING_UID, callingUid);
+ proto.write(JobStatusDumpProto.TAG, tag);
+ proto.write(JobStatusDumpProto.SOURCE_UID, getSourceUid());
+ proto.write(JobStatusDumpProto.SOURCE_USER_ID, getSourceUserId());
+ proto.write(JobStatusDumpProto.SOURCE_PACKAGE_NAME, getSourcePackageName());
+ proto.write(JobStatusDumpProto.INTERNAL_FLAGS, getInternalFlags());
+
+ if (full) {
+ final long jiToken = proto.start(JobStatusDumpProto.JOB_INFO);
+
+ job.getService().writeToProto(proto, JobStatusDumpProto.JobInfo.SERVICE);
+
+ proto.write(JobStatusDumpProto.JobInfo.IS_PERIODIC, job.isPeriodic());
+ proto.write(JobStatusDumpProto.JobInfo.PERIOD_INTERVAL_MS, job.getIntervalMillis());
+ proto.write(JobStatusDumpProto.JobInfo.PERIOD_FLEX_MS, job.getFlexMillis());
+
+ proto.write(JobStatusDumpProto.JobInfo.IS_PERSISTED, job.isPersisted());
+ proto.write(JobStatusDumpProto.JobInfo.PRIORITY, job.getPriority());
+ proto.write(JobStatusDumpProto.JobInfo.FLAGS, job.getFlags());
+
+ proto.write(JobStatusDumpProto.JobInfo.REQUIRES_CHARGING, job.isRequireCharging());
+ proto.write(JobStatusDumpProto.JobInfo.REQUIRES_BATTERY_NOT_LOW, job.isRequireBatteryNotLow());
+ proto.write(JobStatusDumpProto.JobInfo.REQUIRES_DEVICE_IDLE, job.isRequireDeviceIdle());
+
+ if (job.getTriggerContentUris() != null) {
+ for (int i = 0; i < job.getTriggerContentUris().length; i++) {
+ final long tcuToken = proto.start(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_URIS);
+ JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
+
+ proto.write(JobStatusDumpProto.JobInfo.TriggerContentUri.FLAGS, trig.getFlags());
+ Uri u = trig.getUri();
+ if (u != null) {
+ proto.write(JobStatusDumpProto.JobInfo.TriggerContentUri.URI, u.toString());
+ }
+
+ proto.end(tcuToken);
+ }
+ if (job.getTriggerContentUpdateDelay() >= 0) {
+ proto.write(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_UPDATE_DELAY_MS,
+ job.getTriggerContentUpdateDelay());
+ }
+ if (job.getTriggerContentMaxDelay() >= 0) {
+ proto.write(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_MAX_DELAY_MS,
+ job.getTriggerContentMaxDelay());
+ }
+ }
+ if (job.getExtras() != null && !job.getExtras().maybeIsEmpty()) {
+ job.getExtras().writeToProto(proto, JobStatusDumpProto.JobInfo.EXTRAS);
+ }
+ if (job.getTransientExtras() != null && !job.getTransientExtras().maybeIsEmpty()) {
+ job.getTransientExtras().writeToProto(proto, JobStatusDumpProto.JobInfo.TRANSIENT_EXTRAS);
+ }
+ if (job.getClipData() != null) {
+ job.getClipData().writeToProto(proto, JobStatusDumpProto.JobInfo.CLIP_DATA);
+ }
+ if (uriPerms != null) {
+ uriPerms.dump(proto, JobStatusDumpProto.JobInfo.GRANTED_URI_PERMISSIONS);
+ }
+ if (job.getRequiredNetwork() != null) {
+ job.getRequiredNetwork().writeToProto(proto, JobStatusDumpProto.JobInfo.REQUIRED_NETWORK);
+ }
+ if (totalNetworkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ proto.write(JobStatusDumpProto.JobInfo.TOTAL_NETWORK_BYTES, totalNetworkBytes);
+ }
+ proto.write(JobStatusDumpProto.JobInfo.MIN_LATENCY_MS, job.getMinLatencyMillis());
+ proto.write(JobStatusDumpProto.JobInfo.MAX_EXECUTION_DELAY_MS, job.getMaxExecutionDelayMillis());
+
+ final long bpToken = proto.start(JobStatusDumpProto.JobInfo.BACKOFF_POLICY);
+ proto.write(JobStatusDumpProto.JobInfo.Backoff.POLICY, job.getBackoffPolicy());
+ proto.write(JobStatusDumpProto.JobInfo.Backoff.INITIAL_BACKOFF_MS,
+ job.getInitialBackoffMillis());
+ proto.end(bpToken);
+
+ proto.write(JobStatusDumpProto.JobInfo.HAS_EARLY_CONSTRAINT, job.hasEarlyConstraint());
+ proto.write(JobStatusDumpProto.JobInfo.HAS_LATE_CONSTRAINT, job.hasLateConstraint());
+
+ proto.end(jiToken);
+ }
+
+ dumpConstraints(proto, JobStatusDumpProto.REQUIRED_CONSTRAINTS, requiredConstraints);
+ if (full) {
+ dumpConstraints(proto, JobStatusDumpProto.SATISFIED_CONSTRAINTS, satisfiedConstraints);
+ dumpConstraints(proto, JobStatusDumpProto.UNSATISFIED_CONSTRAINTS,
+ (requiredConstraints & ~satisfiedConstraints));
+ proto.write(JobStatusDumpProto.IS_DOZE_WHITELISTED, dozeWhitelisted);
+ }
+
+ // Tracking controllers
+ if ((trackingControllers&TRACKING_BATTERY) != 0) {
+ proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
+ JobStatusDumpProto.TRACKING_BATTERY);
+ }
+ if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) {
+ proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
+ JobStatusDumpProto.TRACKING_CONNECTIVITY);
+ }
+ if ((trackingControllers&TRACKING_CONTENT) != 0) {
+ proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
+ JobStatusDumpProto.TRACKING_CONTENT);
+ }
+ if ((trackingControllers&TRACKING_IDLE) != 0) {
+ proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
+ JobStatusDumpProto.TRACKING_IDLE);
+ }
+ if ((trackingControllers&TRACKING_STORAGE) != 0) {
+ proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
+ JobStatusDumpProto.TRACKING_STORAGE);
+ }
+ if ((trackingControllers&TRACKING_TIME) != 0) {
+ proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
+ JobStatusDumpProto.TRACKING_TIME);
+ }
+
+ if (changedAuthorities != null) {
+ for (int k = 0; k < changedAuthorities.size(); k++) {
+ proto.write(JobStatusDumpProto.CHANGED_AUTHORITIES, changedAuthorities.valueAt(k));
+ }
+ }
+ if (changedUris != null) {
+ for (int i = 0; i < changedUris.size(); i++) {
+ Uri u = changedUris.valueAt(i);
+ proto.write(JobStatusDumpProto.CHANGED_URIS, u.toString());
+ }
+ }
+
+ if (network != null) {
+ network.writeToProto(proto, JobStatusDumpProto.NETWORK);
+ }
+
+ if (pendingWork != null && pendingWork.size() > 0) {
+ for (int i = 0; i < pendingWork.size(); i++) {
+ dumpJobWorkItem(proto, JobStatusDumpProto.PENDING_WORK, pendingWork.get(i));
+ }
+ }
+ if (executingWork != null && executingWork.size() > 0) {
+ for (int i = 0; i < executingWork.size(); i++) {
+ dumpJobWorkItem(proto, JobStatusDumpProto.EXECUTING_WORK, executingWork.get(i));
+ }
+ }
+
+ proto.write(JobStatusDumpProto.STANDBY_BUCKET, standbyBucket);
+ proto.write(JobStatusDumpProto.ENQUEUE_DURATION_MS, elapsedRealtimeMillis - enqueueTime);
+ if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME) {
+ proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS, 0);
+ } else {
+ proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS,
+ earliestRunTimeElapsedMillis - elapsedRealtimeMillis);
+ }
+ if (latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) {
+ proto.write(JobStatusDumpProto.TIME_UNTIL_LATEST_RUNTIME_MS, 0);
+ } else {
+ proto.write(JobStatusDumpProto.TIME_UNTIL_LATEST_RUNTIME_MS,
+ latestRunTimeElapsedMillis - elapsedRealtimeMillis);
+ }
+
+ proto.write(JobStatusDumpProto.NUM_FAILURES, numFailures);
+ proto.write(JobStatusDumpProto.LAST_SUCCESSFUL_RUN_TIME, mLastSuccessfulRunTime);
+ proto.write(JobStatusDumpProto.LAST_FAILED_RUN_TIME, mLastFailedRunTime);
+
+ proto.end(token);
+ }
}
diff --git a/com/android/server/job/controllers/StateController.java b/com/android/server/job/controllers/StateController.java
index 497faab8..d3055e6f 100644
--- a/com/android/server/job/controllers/StateController.java
+++ b/com/android/server/job/controllers/StateController.java
@@ -17,6 +17,7 @@
package com.android.server.job.controllers;
import android.content.Context;
+import android.util.proto.ProtoOutputStream;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateChangedListener;
@@ -65,4 +66,6 @@ public abstract class StateController {
}
public abstract void dumpControllerStateLocked(PrintWriter pw, int filterUid);
+ public abstract void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
+ int filterUid);
}
diff --git a/com/android/server/job/controllers/StorageController.java b/com/android/server/job/controllers/StorageController.java
index 84782f59..0519b635 100644
--- a/com/android/server/job/controllers/StorageController.java
+++ b/com/android/server/job/controllers/StorageController.java
@@ -25,10 +25,12 @@ import android.content.IntentFilter;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateChangedListener;
+import com.android.server.job.StateControllerProto;
import com.android.server.storage.DeviceStorageMonitorService;
import java.io.PrintWriter;
@@ -119,7 +121,7 @@ public final class StorageController extends StateController {
*/
private boolean mStorageLow;
/** Sequence number of last broadcast. */
- private int mLastBatterySeq = -1;
+ private int mLastStorageSeq = -1;
public StorageTracker() {
}
@@ -139,7 +141,7 @@ public final class StorageController extends StateController {
}
public int getSeq() {
- return mLastBatterySeq;
+ return mLastStorageSeq;
}
@Override
@@ -150,8 +152,8 @@ public final class StorageController extends StateController {
@VisibleForTesting
public void onReceiveInternal(Intent intent) {
final String action = intent.getAction();
- mLastBatterySeq = intent.getIntExtra(DeviceStorageMonitorService.EXTRA_SEQUENCE,
- mLastBatterySeq);
+ mLastStorageSeq = intent.getIntExtra(DeviceStorageMonitorService.EXTRA_SEQUENCE,
+ mLastStorageSeq);
if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
if (DEBUG) {
Slog.d(TAG, "Available storage too low to do work. @ "
@@ -190,4 +192,30 @@ public final class StorageController extends StateController {
pw.println();
}
}
+
+ @Override
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+ final long token = proto.start(fieldId);
+ final long mToken = proto.start(StateControllerProto.STORAGE);
+
+ proto.write(StateControllerProto.StorageController.IS_STORAGE_NOT_LOW,
+ mStorageTracker.isStorageNotLow());
+ proto.write(StateControllerProto.StorageController.LAST_BROADCAST_SEQUENCE_NUMBER,
+ mStorageTracker.getSeq());
+
+ for (int i = 0; i < mTrackedTasks.size(); i++) {
+ final JobStatus js = mTrackedTasks.valueAt(i);
+ if (!js.shouldDump(filterUid)) {
+ continue;
+ }
+ final long jsToken = proto.start(StateControllerProto.StorageController.TRACKED_JOBS);
+ js.writeToShortProto(proto, StateControllerProto.StorageController.TrackedJob.INFO);
+ proto.write(StateControllerProto.StorageController.TrackedJob.SOURCE_UID,
+ js.getSourceUid());
+ proto.end(jsToken);
+ }
+
+ proto.end(mToken);
+ proto.end(token);
+ }
}
diff --git a/com/android/server/job/controllers/TimeController.java b/com/android/server/job/controllers/TimeController.java
index cb9e43a1..bbee0ebd 100644
--- a/com/android/server/job/controllers/TimeController.java
+++ b/com/android/server/job/controllers/TimeController.java
@@ -25,9 +25,11 @@ import android.os.UserHandle;
import android.os.WorkSource;
import android.util.Slog;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateChangedListener;
+import com.android.server.job.StateControllerProto;
import java.io.PrintWriter;
import java.util.Iterator;
@@ -331,7 +333,7 @@ public final class TimeController extends StateController {
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
final long nowElapsed = sElapsedRealtimeClock.millis();
pw.print("Alarms: now=");
- pw.print(sElapsedRealtimeClock.millis());
+ pw.print(nowElapsed);
pw.println();
pw.print("Next delay alarm in ");
TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw);
@@ -365,4 +367,40 @@ public final class TimeController extends StateController {
pw.println();
}
}
-} \ No newline at end of file
+
+ @Override
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+ final long token = proto.start(fieldId);
+ final long mToken = proto.start(StateControllerProto.TIME);
+
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ proto.write(StateControllerProto.TimeController.NOW_ELAPSED_REALTIME, nowElapsed);
+ proto.write(StateControllerProto.TimeController.TIME_UNTIL_NEXT_DELAY_ALARM_MS,
+ mNextDelayExpiredElapsedMillis - nowElapsed);
+ proto.write(StateControllerProto.TimeController.TIME_UNTIL_NEXT_DEADLINE_ALARM_MS,
+ mNextJobExpiredElapsedMillis - nowElapsed);
+
+ for (JobStatus ts : mTrackedJobs) {
+ if (!ts.shouldDump(filterUid)) {
+ continue;
+ }
+ final long tsToken = proto.start(StateControllerProto.TimeController.TRACKED_JOBS);
+ ts.writeToShortProto(proto, StateControllerProto.TimeController.TrackedJob.INFO);
+
+ proto.write(StateControllerProto.TimeController.TrackedJob.HAS_TIMING_DELAY_CONSTRAINT,
+ ts.hasTimingDelayConstraint());
+ proto.write(StateControllerProto.TimeController.TrackedJob.DELAY_TIME_REMAINING_MS,
+ ts.getEarliestRunTime() - nowElapsed);
+
+ proto.write(StateControllerProto.TimeController.TrackedJob.HAS_DEADLINE_CONSTRAINT,
+ ts.hasDeadlineConstraint());
+ proto.write(StateControllerProto.TimeController.TrackedJob.TIME_REMAINING_UNTIL_DEADLINE_MS,
+ ts.getLatestRunTimeElapsed() - nowElapsed);
+
+ proto.end(tsToken);
+ }
+
+ proto.end(mToken);
+ proto.end(token);
+ }
+}