Loading AconfigFlags.bp +14 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ aconfig_srcjars = [ ":com.android.hardware.input-aconfig-java{.generated_srcjars}", ":com.android.input.flags-aconfig-java{.generated_srcjars}", ":com.android.text.flags-aconfig-java{.generated_srcjars}", ":framework-jobscheduler-job.flags-aconfig-java{.generated_srcjars}", ":telecom_flags_core_java_lib{.generated_srcjars}", ":telephony_flags_core_java_lib{.generated_srcjars}", ":android.companion.virtual.flags-aconfig-java{.generated_srcjars}", Loading Loading @@ -664,6 +665,19 @@ cc_aconfig_library { aconfig_declarations: "device_policy_aconfig_flags", } // JobScheduler aconfig_declarations { name: "framework-jobscheduler-job.flags-aconfig", package: "android.app.job", srcs: ["apex/jobscheduler/framework/aconfig/job.aconfig"], } java_aconfig_library { name: "framework-jobscheduler-job.flags-aconfig-java", aconfig_declarations: "framework-jobscheduler-job.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], } // Notifications aconfig_declarations { name: "android.service.notification.flags-aconfig", Loading apex/jobscheduler/framework/aconfig/job.aconfig 0 → 100644 +8 −0 Original line number Diff line number Diff line package: "android.app.job" flag { name: "job_debug_info_apis" namespace: "backstage_power" description: "Add APIs to let apps attach debug information to jobs" bug: "293491637" } apex/jobscheduler/framework/java/android/app/job/JobInfo.java +198 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.util.TimeUtils.formatDuration; import android.annotation.BytesLong; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; Loading @@ -47,13 +48,17 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; import android.os.Trace; import android.util.ArraySet; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Objects; import java.util.Set; /** * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the Loading Loading @@ -423,6 +428,15 @@ public class JobInfo implements Parcelable { */ public static final int CONSTRAINT_FLAG_STORAGE_NOT_LOW = 1 << 3; /** @hide */ public static final int MAX_NUM_DEBUG_TAGS = 32; /** @hide */ public static final int MAX_DEBUG_TAG_LENGTH = 127; /** @hide */ public static final int MAX_TRACE_TAG_LENGTH = Trace.MAX_SECTION_NAME_LEN; @UnsupportedAppUsage private final int jobId; private final PersistableBundle extras; Loading Loading @@ -454,6 +468,9 @@ public class JobInfo implements Parcelable { private final int mPriority; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final int flags; private final ArraySet<String> mDebugTags; @Nullable private final String mTraceTag; /** * Unique job id associated with this application (uid). This is the same job ID Loading Loading @@ -723,6 +740,33 @@ public class JobInfo implements Parcelable { return backoffPolicy; } /** * @see JobInfo.Builder#addDebugTag(String) */ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS) @NonNull public Set<String> getDebugTags() { return Collections.unmodifiableSet(mDebugTags); } /** * @see JobInfo.Builder#addDebugTag(String) * @hide */ @NonNull public ArraySet<String> getDebugTagsArraySet() { return mDebugTags; } /** * @see JobInfo.Builder#setTraceTag(String) */ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS) @Nullable public String getTraceTag() { return mTraceTag; } /** * @see JobInfo.Builder#setExpedited(boolean) */ Loading Loading @@ -860,6 +904,12 @@ public class JobInfo implements Parcelable { if (flags != j.flags) { return false; } if (!mDebugTags.equals(j.mDebugTags)) { return false; } if (!Objects.equals(mTraceTag, j.mTraceTag)) { return false; } return true; } Loading Loading @@ -904,6 +954,12 @@ public class JobInfo implements Parcelable { hashCode = 31 * hashCode + mBias; hashCode = 31 * hashCode + mPriority; hashCode = 31 * hashCode + flags; if (mDebugTags.size() > 0) { hashCode = 31 * hashCode + mDebugTags.hashCode(); } if (mTraceTag != null) { hashCode = 31 * hashCode + mTraceTag.hashCode(); } return hashCode; } Loading Loading @@ -946,6 +1002,17 @@ public class JobInfo implements Parcelable { mBias = in.readInt(); mPriority = in.readInt(); flags = in.readInt(); final int numDebugTags = in.readInt(); mDebugTags = new ArraySet<>(); for (int i = 0; i < numDebugTags; ++i) { final String tag = in.readString(); if (tag == null) { throw new IllegalStateException("malformed parcel"); } mDebugTags.add(tag.intern()); } final String traceTag = in.readString(); mTraceTag = traceTag == null ? null : traceTag.intern(); } private JobInfo(JobInfo.Builder b) { Loading Loading @@ -978,6 +1045,8 @@ public class JobInfo implements Parcelable { mBias = b.mBias; mPriority = b.mPriority; flags = b.mFlags; mDebugTags = b.mDebugTags; mTraceTag = b.mTraceTag; } @Override Loading Loading @@ -1024,6 +1093,14 @@ public class JobInfo implements Parcelable { out.writeInt(mBias); out.writeInt(mPriority); out.writeInt(this.flags); // Explicitly write out values here to avoid double looping to intern the strings // when unparcelling. final int numDebugTags = mDebugTags.size(); out.writeInt(numDebugTags); for (int i = 0; i < numDebugTags; ++i) { out.writeString(mDebugTags.valueAt(i)); } out.writeString(mTraceTag); } public static final @android.annotation.NonNull Creator<JobInfo> CREATOR = new Creator<JobInfo>() { Loading Loading @@ -1168,6 +1245,8 @@ public class JobInfo implements Parcelable { private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY; /** Easy way to track whether the client has tried to set a back-off policy. */ private boolean mBackoffPolicySet = false; private final ArraySet<String> mDebugTags = new ArraySet<>(); private String mTraceTag; /** * Initialize a new Builder to construct a {@link JobInfo}. Loading Loading @@ -1222,6 +1301,51 @@ public class JobInfo implements Parcelable { mPriority = job.getPriority(); } /** * Add a debug tag to help track what this job is for. The tags may show in debug dumps * or app metrics. Do not put personally identifiable information (PII) in the tag. * <p> * Tags have the following requirements: * <ul> * <li>Tags cannot be more than 127 characters.</li> * <li> * Since leading and trailing whitespace can lead to hard-to-debug issues, * tags should not include leading or trailing whitespace. * All tags will be {@link String#trim() trimmed}. * </li> * <li>An empty String (after trimming) is not allowed.</li> * <li>Should not have personally identifiable information (PII).</li> * <li>A job cannot have more than 32 tags.</li> * </ul> * * @param tag A debug tag that helps describe what the job is for. * @return This object for method chaining */ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS) @NonNull public Builder addDebugTag(@NonNull String tag) { mDebugTags.add(validateDebugTag(tag)); return this; } /** @hide */ @NonNull public void addDebugTags(@NonNull Set<String> tags) { mDebugTags.addAll(tags); } /** * Remove a tag set via {@link #addDebugTag(String)}. * @param tag The tag to remove * @return This object for method chaining */ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS) @NonNull public Builder removeDebugTag(@NonNull String tag) { mDebugTags.remove(tag); return this; } /** @hide */ @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) Loading Loading @@ -1996,6 +2120,24 @@ public class JobInfo implements Parcelable { return this; } /** * Set a tag that will be used in {@link android.os.Trace traces}. * Since this is a trace tag, it must follow the rules set in * {@link android.os.Trace#beginSection(String)}, such as it cannot be more * than 127 Unicode code units. * Additionally, since leading and trailing whitespace can lead to hard-to-debug issues, * they will be {@link String#trim() trimmed}. * An empty String (after trimming) is not allowed. * @param traceTag The tag to use in traces. * @return This object for method chaining */ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS) @NonNull public Builder setTraceTag(@Nullable String traceTag) { mTraceTag = validateTraceTag(traceTag); return this; } /** * @return The job object to hand to the JobScheduler. This object is immutable. */ Loading Loading @@ -2209,6 +2351,62 @@ public class JobInfo implements Parcelable { "A user-initiated data transfer job must specify a valid network type"); } } if (mDebugTags.size() > MAX_NUM_DEBUG_TAGS) { throw new IllegalArgumentException( "Can't have more than " + MAX_NUM_DEBUG_TAGS + " tags"); } final ArraySet<String> validatedDebugTags = new ArraySet<>(); for (int i = 0; i < mDebugTags.size(); ++i) { validatedDebugTags.add(validateDebugTag(mDebugTags.valueAt(i))); } mDebugTags.clear(); mDebugTags.addAll(validatedDebugTags); validateTraceTag(mTraceTag); } /** * Returns a sanitized debug tag if valid, or throws an exception if not. * @hide */ @NonNull public static String validateDebugTag(@Nullable String debugTag) { if (debugTag == null) { throw new NullPointerException("debug tag cannot be null"); } debugTag = debugTag.trim(); if (debugTag.isEmpty()) { throw new IllegalArgumentException("debug tag cannot be empty"); } if (debugTag.length() > MAX_DEBUG_TAG_LENGTH) { throw new IllegalArgumentException( "debug tag cannot be more than " + MAX_DEBUG_TAG_LENGTH + " characters"); } return debugTag.intern(); } /** * Returns a sanitized trace tag if valid, or throws an exception if not. * @hide */ @Nullable public static String validateTraceTag(@Nullable String traceTag) { if (traceTag == null) { return null; } traceTag = traceTag.trim(); if (traceTag.isEmpty()) { throw new IllegalArgumentException("trace tag cannot be empty"); } if (traceTag.length() > MAX_TRACE_TAG_LENGTH) { throw new IllegalArgumentException( "traceTag tag cannot be more than " + MAX_TRACE_TAG_LENGTH + " characters"); } if (traceTag.contains("|") || traceTag.contains("\n") || traceTag.contains("\0")) { throw new IllegalArgumentException("Trace tag cannot contain |, \\n, or \\0"); } return traceTag.intern(); } /** Loading apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +9 −0 Original line number Diff line number Diff line Loading @@ -557,6 +557,11 @@ public final class JobServiceContext implements ServiceConnection { Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler", traceTag, getId()); } if (job.getAppTraceTag() != null) { // Use the job's ID to distinguish traces since the ID will be unique per app. Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, "JobScheduler", job.getAppTraceTag(), job.getJobId()); } try { mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid()); } catch (RemoteException e) { Loading Loading @@ -1616,6 +1621,10 @@ public final class JobServiceContext implements ServiceConnection { Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler", getId()); } if (completedJob.getAppTraceTag() != null) { Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, "JobScheduler", completedJob.getJobId()); } try { mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(), loggingInternalStopReason); Loading apex/jobscheduler/service/java/com/android/server/job/JobStore.java +62 −0 Original line number Diff line number Diff line Loading @@ -510,6 +510,8 @@ public final class JobStore { private static final String XML_TAG_ONEOFF = "one-off"; private static final String XML_TAG_EXTRAS = "extras"; private static final String XML_TAG_JOB_WORK_ITEM = "job-work-item"; private static final String XML_TAG_DEBUG_INFO = "debug-info"; private static final String XML_TAG_DEBUG_TAG = "debug-tag"; private void migrateJobFilesAsync() { synchronized (mLock) { Loading Loading @@ -805,6 +807,7 @@ public final class JobStore { writeExecutionCriteriaToXml(out, jobStatus); writeBundleToXml(jobStatus.getJob().getExtras(), out); writeJobWorkItemsToXml(out, jobStatus); writeDebugInfoToXml(out, jobStatus); out.endTag(null, XML_TAG_JOB); numJobs++; Loading Loading @@ -991,6 +994,26 @@ public final class JobStore { } } private void writeDebugInfoToXml(@NonNull TypedXmlSerializer out, @NonNull JobStatus jobStatus) throws IOException, XmlPullParserException { final ArraySet<String> debugTags = jobStatus.getJob().getDebugTagsArraySet(); final int numTags = debugTags.size(); final String traceTag = jobStatus.getJob().getTraceTag(); if (traceTag == null && numTags == 0) { return; } out.startTag(null, XML_TAG_DEBUG_INFO); if (traceTag != null) { out.attribute(null, "trace-tag", traceTag); } for (int i = 0; i < numTags; ++i) { out.startTag(null, XML_TAG_DEBUG_TAG); out.attribute(null, "tag", debugTags.valueAt(i)); out.endTag(null, XML_TAG_DEBUG_TAG); } out.endTag(null, XML_TAG_DEBUG_INFO); } private void writeJobWorkItemsToXml(@NonNull TypedXmlSerializer out, @NonNull JobStatus jobStatus) throws IOException, XmlPullParserException { // Write executing first since they're technically at the front of the queue. Loading Loading @@ -1449,6 +1472,18 @@ public final class JobStore { jobWorkItems = readJobWorkItemsFromXml(parser); } if (eventType == XmlPullParser.START_TAG && XML_TAG_DEBUG_INFO.equals(parser.getName())) { try { jobBuilder.setTraceTag(parser.getAttributeValue(null, "trace-tag")); } catch (Exception e) { Slog.wtf(TAG, "Invalid trace tag persisted to disk", e); } parser.next(); jobBuilder.addDebugTags(readDebugTagsFromXml(parser)); eventType = parser.nextTag(); // Consume </debug-info> } final JobInfo builtJob; try { // Don't perform prefetch-deadline check here. Apps targeting S- shouldn't have Loading Loading @@ -1721,6 +1756,33 @@ public final class JobStore { return null; } } @NonNull private Set<String> readDebugTagsFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { Set<String> debugTags = new ArraySet<>(); for (int eventType = parser.getEventType(); eventType != XmlPullParser.END_DOCUMENT; eventType = parser.next()) { final String tagName = parser.getName(); if (!XML_TAG_DEBUG_TAG.equals(tagName)) { // We're no longer operating with debug tags. break; } if (debugTags.size() < JobInfo.MAX_NUM_DEBUG_TAGS) { final String debugTag; try { debugTag = JobInfo.validateDebugTag(parser.getAttributeValue(null, "tag")); } catch (Exception e) { Slog.wtf(TAG, "Invalid debug tag persisted to disk", e); continue; } debugTags.add(debugTag); } } return debugTags; } } /** Set of all tracked jobs. */ Loading Loading
AconfigFlags.bp +14 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ aconfig_srcjars = [ ":com.android.hardware.input-aconfig-java{.generated_srcjars}", ":com.android.input.flags-aconfig-java{.generated_srcjars}", ":com.android.text.flags-aconfig-java{.generated_srcjars}", ":framework-jobscheduler-job.flags-aconfig-java{.generated_srcjars}", ":telecom_flags_core_java_lib{.generated_srcjars}", ":telephony_flags_core_java_lib{.generated_srcjars}", ":android.companion.virtual.flags-aconfig-java{.generated_srcjars}", Loading Loading @@ -664,6 +665,19 @@ cc_aconfig_library { aconfig_declarations: "device_policy_aconfig_flags", } // JobScheduler aconfig_declarations { name: "framework-jobscheduler-job.flags-aconfig", package: "android.app.job", srcs: ["apex/jobscheduler/framework/aconfig/job.aconfig"], } java_aconfig_library { name: "framework-jobscheduler-job.flags-aconfig-java", aconfig_declarations: "framework-jobscheduler-job.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], } // Notifications aconfig_declarations { name: "android.service.notification.flags-aconfig", Loading
apex/jobscheduler/framework/aconfig/job.aconfig 0 → 100644 +8 −0 Original line number Diff line number Diff line package: "android.app.job" flag { name: "job_debug_info_apis" namespace: "backstage_power" description: "Add APIs to let apps attach debug information to jobs" bug: "293491637" }
apex/jobscheduler/framework/java/android/app/job/JobInfo.java +198 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.util.TimeUtils.formatDuration; import android.annotation.BytesLong; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; Loading @@ -47,13 +48,17 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; import android.os.Trace; import android.util.ArraySet; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Objects; import java.util.Set; /** * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the Loading Loading @@ -423,6 +428,15 @@ public class JobInfo implements Parcelable { */ public static final int CONSTRAINT_FLAG_STORAGE_NOT_LOW = 1 << 3; /** @hide */ public static final int MAX_NUM_DEBUG_TAGS = 32; /** @hide */ public static final int MAX_DEBUG_TAG_LENGTH = 127; /** @hide */ public static final int MAX_TRACE_TAG_LENGTH = Trace.MAX_SECTION_NAME_LEN; @UnsupportedAppUsage private final int jobId; private final PersistableBundle extras; Loading Loading @@ -454,6 +468,9 @@ public class JobInfo implements Parcelable { private final int mPriority; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final int flags; private final ArraySet<String> mDebugTags; @Nullable private final String mTraceTag; /** * Unique job id associated with this application (uid). This is the same job ID Loading Loading @@ -723,6 +740,33 @@ public class JobInfo implements Parcelable { return backoffPolicy; } /** * @see JobInfo.Builder#addDebugTag(String) */ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS) @NonNull public Set<String> getDebugTags() { return Collections.unmodifiableSet(mDebugTags); } /** * @see JobInfo.Builder#addDebugTag(String) * @hide */ @NonNull public ArraySet<String> getDebugTagsArraySet() { return mDebugTags; } /** * @see JobInfo.Builder#setTraceTag(String) */ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS) @Nullable public String getTraceTag() { return mTraceTag; } /** * @see JobInfo.Builder#setExpedited(boolean) */ Loading Loading @@ -860,6 +904,12 @@ public class JobInfo implements Parcelable { if (flags != j.flags) { return false; } if (!mDebugTags.equals(j.mDebugTags)) { return false; } if (!Objects.equals(mTraceTag, j.mTraceTag)) { return false; } return true; } Loading Loading @@ -904,6 +954,12 @@ public class JobInfo implements Parcelable { hashCode = 31 * hashCode + mBias; hashCode = 31 * hashCode + mPriority; hashCode = 31 * hashCode + flags; if (mDebugTags.size() > 0) { hashCode = 31 * hashCode + mDebugTags.hashCode(); } if (mTraceTag != null) { hashCode = 31 * hashCode + mTraceTag.hashCode(); } return hashCode; } Loading Loading @@ -946,6 +1002,17 @@ public class JobInfo implements Parcelable { mBias = in.readInt(); mPriority = in.readInt(); flags = in.readInt(); final int numDebugTags = in.readInt(); mDebugTags = new ArraySet<>(); for (int i = 0; i < numDebugTags; ++i) { final String tag = in.readString(); if (tag == null) { throw new IllegalStateException("malformed parcel"); } mDebugTags.add(tag.intern()); } final String traceTag = in.readString(); mTraceTag = traceTag == null ? null : traceTag.intern(); } private JobInfo(JobInfo.Builder b) { Loading Loading @@ -978,6 +1045,8 @@ public class JobInfo implements Parcelable { mBias = b.mBias; mPriority = b.mPriority; flags = b.mFlags; mDebugTags = b.mDebugTags; mTraceTag = b.mTraceTag; } @Override Loading Loading @@ -1024,6 +1093,14 @@ public class JobInfo implements Parcelable { out.writeInt(mBias); out.writeInt(mPriority); out.writeInt(this.flags); // Explicitly write out values here to avoid double looping to intern the strings // when unparcelling. final int numDebugTags = mDebugTags.size(); out.writeInt(numDebugTags); for (int i = 0; i < numDebugTags; ++i) { out.writeString(mDebugTags.valueAt(i)); } out.writeString(mTraceTag); } public static final @android.annotation.NonNull Creator<JobInfo> CREATOR = new Creator<JobInfo>() { Loading Loading @@ -1168,6 +1245,8 @@ public class JobInfo implements Parcelable { private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY; /** Easy way to track whether the client has tried to set a back-off policy. */ private boolean mBackoffPolicySet = false; private final ArraySet<String> mDebugTags = new ArraySet<>(); private String mTraceTag; /** * Initialize a new Builder to construct a {@link JobInfo}. Loading Loading @@ -1222,6 +1301,51 @@ public class JobInfo implements Parcelable { mPriority = job.getPriority(); } /** * Add a debug tag to help track what this job is for. The tags may show in debug dumps * or app metrics. Do not put personally identifiable information (PII) in the tag. * <p> * Tags have the following requirements: * <ul> * <li>Tags cannot be more than 127 characters.</li> * <li> * Since leading and trailing whitespace can lead to hard-to-debug issues, * tags should not include leading or trailing whitespace. * All tags will be {@link String#trim() trimmed}. * </li> * <li>An empty String (after trimming) is not allowed.</li> * <li>Should not have personally identifiable information (PII).</li> * <li>A job cannot have more than 32 tags.</li> * </ul> * * @param tag A debug tag that helps describe what the job is for. * @return This object for method chaining */ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS) @NonNull public Builder addDebugTag(@NonNull String tag) { mDebugTags.add(validateDebugTag(tag)); return this; } /** @hide */ @NonNull public void addDebugTags(@NonNull Set<String> tags) { mDebugTags.addAll(tags); } /** * Remove a tag set via {@link #addDebugTag(String)}. * @param tag The tag to remove * @return This object for method chaining */ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS) @NonNull public Builder removeDebugTag(@NonNull String tag) { mDebugTags.remove(tag); return this; } /** @hide */ @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) Loading Loading @@ -1996,6 +2120,24 @@ public class JobInfo implements Parcelable { return this; } /** * Set a tag that will be used in {@link android.os.Trace traces}. * Since this is a trace tag, it must follow the rules set in * {@link android.os.Trace#beginSection(String)}, such as it cannot be more * than 127 Unicode code units. * Additionally, since leading and trailing whitespace can lead to hard-to-debug issues, * they will be {@link String#trim() trimmed}. * An empty String (after trimming) is not allowed. * @param traceTag The tag to use in traces. * @return This object for method chaining */ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS) @NonNull public Builder setTraceTag(@Nullable String traceTag) { mTraceTag = validateTraceTag(traceTag); return this; } /** * @return The job object to hand to the JobScheduler. This object is immutable. */ Loading Loading @@ -2209,6 +2351,62 @@ public class JobInfo implements Parcelable { "A user-initiated data transfer job must specify a valid network type"); } } if (mDebugTags.size() > MAX_NUM_DEBUG_TAGS) { throw new IllegalArgumentException( "Can't have more than " + MAX_NUM_DEBUG_TAGS + " tags"); } final ArraySet<String> validatedDebugTags = new ArraySet<>(); for (int i = 0; i < mDebugTags.size(); ++i) { validatedDebugTags.add(validateDebugTag(mDebugTags.valueAt(i))); } mDebugTags.clear(); mDebugTags.addAll(validatedDebugTags); validateTraceTag(mTraceTag); } /** * Returns a sanitized debug tag if valid, or throws an exception if not. * @hide */ @NonNull public static String validateDebugTag(@Nullable String debugTag) { if (debugTag == null) { throw new NullPointerException("debug tag cannot be null"); } debugTag = debugTag.trim(); if (debugTag.isEmpty()) { throw new IllegalArgumentException("debug tag cannot be empty"); } if (debugTag.length() > MAX_DEBUG_TAG_LENGTH) { throw new IllegalArgumentException( "debug tag cannot be more than " + MAX_DEBUG_TAG_LENGTH + " characters"); } return debugTag.intern(); } /** * Returns a sanitized trace tag if valid, or throws an exception if not. * @hide */ @Nullable public static String validateTraceTag(@Nullable String traceTag) { if (traceTag == null) { return null; } traceTag = traceTag.trim(); if (traceTag.isEmpty()) { throw new IllegalArgumentException("trace tag cannot be empty"); } if (traceTag.length() > MAX_TRACE_TAG_LENGTH) { throw new IllegalArgumentException( "traceTag tag cannot be more than " + MAX_TRACE_TAG_LENGTH + " characters"); } if (traceTag.contains("|") || traceTag.contains("\n") || traceTag.contains("\0")) { throw new IllegalArgumentException("Trace tag cannot contain |, \\n, or \\0"); } return traceTag.intern(); } /** Loading
apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +9 −0 Original line number Diff line number Diff line Loading @@ -557,6 +557,11 @@ public final class JobServiceContext implements ServiceConnection { Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler", traceTag, getId()); } if (job.getAppTraceTag() != null) { // Use the job's ID to distinguish traces since the ID will be unique per app. Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, "JobScheduler", job.getAppTraceTag(), job.getJobId()); } try { mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid()); } catch (RemoteException e) { Loading Loading @@ -1616,6 +1621,10 @@ public final class JobServiceContext implements ServiceConnection { Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler", getId()); } if (completedJob.getAppTraceTag() != null) { Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, "JobScheduler", completedJob.getJobId()); } try { mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(), loggingInternalStopReason); Loading
apex/jobscheduler/service/java/com/android/server/job/JobStore.java +62 −0 Original line number Diff line number Diff line Loading @@ -510,6 +510,8 @@ public final class JobStore { private static final String XML_TAG_ONEOFF = "one-off"; private static final String XML_TAG_EXTRAS = "extras"; private static final String XML_TAG_JOB_WORK_ITEM = "job-work-item"; private static final String XML_TAG_DEBUG_INFO = "debug-info"; private static final String XML_TAG_DEBUG_TAG = "debug-tag"; private void migrateJobFilesAsync() { synchronized (mLock) { Loading Loading @@ -805,6 +807,7 @@ public final class JobStore { writeExecutionCriteriaToXml(out, jobStatus); writeBundleToXml(jobStatus.getJob().getExtras(), out); writeJobWorkItemsToXml(out, jobStatus); writeDebugInfoToXml(out, jobStatus); out.endTag(null, XML_TAG_JOB); numJobs++; Loading Loading @@ -991,6 +994,26 @@ public final class JobStore { } } private void writeDebugInfoToXml(@NonNull TypedXmlSerializer out, @NonNull JobStatus jobStatus) throws IOException, XmlPullParserException { final ArraySet<String> debugTags = jobStatus.getJob().getDebugTagsArraySet(); final int numTags = debugTags.size(); final String traceTag = jobStatus.getJob().getTraceTag(); if (traceTag == null && numTags == 0) { return; } out.startTag(null, XML_TAG_DEBUG_INFO); if (traceTag != null) { out.attribute(null, "trace-tag", traceTag); } for (int i = 0; i < numTags; ++i) { out.startTag(null, XML_TAG_DEBUG_TAG); out.attribute(null, "tag", debugTags.valueAt(i)); out.endTag(null, XML_TAG_DEBUG_TAG); } out.endTag(null, XML_TAG_DEBUG_INFO); } private void writeJobWorkItemsToXml(@NonNull TypedXmlSerializer out, @NonNull JobStatus jobStatus) throws IOException, XmlPullParserException { // Write executing first since they're technically at the front of the queue. Loading Loading @@ -1449,6 +1472,18 @@ public final class JobStore { jobWorkItems = readJobWorkItemsFromXml(parser); } if (eventType == XmlPullParser.START_TAG && XML_TAG_DEBUG_INFO.equals(parser.getName())) { try { jobBuilder.setTraceTag(parser.getAttributeValue(null, "trace-tag")); } catch (Exception e) { Slog.wtf(TAG, "Invalid trace tag persisted to disk", e); } parser.next(); jobBuilder.addDebugTags(readDebugTagsFromXml(parser)); eventType = parser.nextTag(); // Consume </debug-info> } final JobInfo builtJob; try { // Don't perform prefetch-deadline check here. Apps targeting S- shouldn't have Loading Loading @@ -1721,6 +1756,33 @@ public final class JobStore { return null; } } @NonNull private Set<String> readDebugTagsFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { Set<String> debugTags = new ArraySet<>(); for (int eventType = parser.getEventType(); eventType != XmlPullParser.END_DOCUMENT; eventType = parser.next()) { final String tagName = parser.getName(); if (!XML_TAG_DEBUG_TAG.equals(tagName)) { // We're no longer operating with debug tags. break; } if (debugTags.size() < JobInfo.MAX_NUM_DEBUG_TAGS) { final String debugTag; try { debugTag = JobInfo.validateDebugTag(parser.getAttributeValue(null, "tag")); } catch (Exception e) { Slog.wtf(TAG, "Invalid debug tag persisted to disk", e); continue; } debugTags.add(debugTag); } } return debugTags; } } /** Set of all tracked jobs. */ Loading