diff options
author | Jeff Davidson <jpd@google.com> | 2018-02-08 15:30:06 -0800 |
---|---|---|
committer | Jeff Davidson <jpd@google.com> | 2018-02-08 15:30:06 -0800 |
commit | a192cc2a132cb0ee8588e2df755563ec7008c179 (patch) | |
tree | 380e4db22df19c819bd37df34bf06e7568916a50 /com/android/server/job/controllers | |
parent | 98fe7819c6d14f4f464a5cac047f9e82dee5da58 (diff) | |
download | android-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')
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); + } +} |