Add logging to statsd for job constraint changes.

Bug: 117846754
Bug: 111423978
Bug: 120941744
Test: `make statsd_testdrive &&
./out/host/linux-x86/bin/statsd_testdrive 150` and check output

Change-Id: Iadfdb07171b8d4f99b8f57008cc7c1fc2865bb8e
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 8e56bef..e84eced 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -33,6 +33,7 @@
 import "frameworks/base/core/proto/android/os/enums.proto";
 import "frameworks/base/core/proto/android/server/connectivity/data_stall_event.proto";
 import "frameworks/base/core/proto/android/server/enums.proto";
+import "frameworks/base/core/proto/android/server/job/enums.proto";
 import "frameworks/base/core/proto/android/server/location/enums.proto";
 import "frameworks/base/core/proto/android/service/procstats_enum.proto";
 import "frameworks/base/core/proto/android/service/usb.proto";
@@ -213,6 +214,7 @@
         WatchdogRollbackOccurred watchdog_rollback_occurred = 147;
         BiometricHalDeathReported biometric_hal_death_reported = 148;
         BubbleUIChanged bubble_ui_changed = 149;
+        ScheduledJobConstraintChanged scheduled_job_constraint_changed = 150;
     }
 
     // Pulled events will start at field 10000.
@@ -4699,3 +4701,24 @@
     optional float normalized_x_position = 7;
     optional float normalized_y_position = 8;
 }
+
+/**
+ * Logs that a constraint for a scheduled job has changed.
+ *
+ * Logged from:
+ *     frameworks/base/services/core/java/com/android/server/job/controllers/JobStatus.java
+ */
+message ScheduledJobConstraintChanged {
+    repeated AttributionNode attribution_node = 1;
+
+    // Name of the job.
+    optional string job_name = 2;
+
+    optional com.android.server.job.ConstraintEnum constraint = 3;
+
+    enum State {
+        UNSATISFIED = 0;
+        SATISFIED = 1;
+    }
+    optional State state = 4;
+}
diff --git a/core/proto/android/app/job/enums.proto b/core/proto/android/app/job/enums.proto
index bba8328..f702b3e 100644
--- a/core/proto/android/app/job/enums.proto
+++ b/core/proto/android/app/job/enums.proto
@@ -18,6 +18,9 @@
 
 package android.app.job;
 
+// This file is for JobScheduler enums inside the app directory. If you're
+// adding enums for system-server-side code, use the file in
+// frameworks/base/core/proto/android/server/job.
 option java_outer_classname = "JobProtoEnums";
 option java_multiple_files = true;
 
diff --git a/core/proto/android/server/job/enums.proto b/core/proto/android/server/job/enums.proto
new file mode 100644
index 0000000..50fc031
--- /dev/null
+++ b/core/proto/android/server/job/enums.proto
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package com.android.server.job;
+
+// This file is for JobScheduler enums inside the server directory. If you're
+// adding enums for app-side code, use the file in
+// frameworks/base/core/proto/android/app/job.
+option java_outer_classname = "JobServerProtoEnums";
+option java_multiple_files = true;
+
+// Set of constraints that a job potentially needs satisfied before it can run.
+// Defined in
+// frameworks/base/services/core/java/com/android/server/job/controllers/JobStatus.java
+enum ConstraintEnum {
+    CONSTRAINT_UNKNOWN = 0;
+    CONSTRAINT_CHARGING = 1;
+    CONSTRAINT_BATTERY_NOT_LOW = 2;
+    CONSTRAINT_STORAGE_NOT_LOW = 3;
+    CONSTRAINT_TIMING_DELAY = 4;
+    CONSTRAINT_DEADLINE = 5;
+    CONSTRAINT_IDLE = 6;
+    CONSTRAINT_CONNECTIVITY = 7;
+    CONSTRAINT_CONTENT_TRIGGER = 8;
+    CONSTRAINT_DEVICE_NOT_DOZING = 9;
+    CONSTRAINT_WITHIN_QUOTA = 10;
+    CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 11;
+}
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 188769d..6c9d13a 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -29,6 +29,7 @@
 import "frameworks/base/core/proto/android/os/bundle.proto";
 import "frameworks/base/core/proto/android/os/persistablebundle.proto";
 import "frameworks/base/core/proto/android/server/forceappstandbytracker.proto";
+import "frameworks/base/core/proto/android/server/job/enums.proto";
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 // Next tag: 21
@@ -757,21 +758,9 @@
     }
     optional JobInfo job_info = 6;
 
-    enum Constraint {
-        CONSTRAINT_CHARGING = 1;
-        CONSTRAINT_BATTERY_NOT_LOW = 2;
-        CONSTRAINT_STORAGE_NOT_LOW = 3;
-        CONSTRAINT_TIMING_DELAY = 4;
-        CONSTRAINT_DEADLINE = 5;
-        CONSTRAINT_IDLE = 6;
-        CONSTRAINT_CONNECTIVITY = 7;
-        CONSTRAINT_CONTENT_TRIGGER = 8;
-        CONSTRAINT_DEVICE_NOT_DOZING = 9;
-        CONSTRAINT_WITHIN_QUOTA = 10;
-    }
-    repeated Constraint required_constraints = 7;
-    repeated Constraint satisfied_constraints = 8;
-    repeated Constraint unsatisfied_constraints = 9;
+    repeated ConstraintEnum required_constraints = 7;
+    repeated ConstraintEnum satisfied_constraints = 8;
+    repeated ConstraintEnum unsatisfied_constraints = 9;
     optional bool is_doze_whitelisted = 10;
     optional bool is_uid_active = 26;
 
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 82bfa51..ed1a6d0 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -33,6 +33,7 @@
 import android.util.ArraySet;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
@@ -40,6 +41,7 @@
 import com.android.server.job.GrantedUriPermissions;
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobServerProtoEnums;
 import com.android.server.job.JobStatusDumpProto;
 import com.android.server.job.JobStatusShortInfoProto;
 
@@ -80,6 +82,28 @@
     static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24;
     static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1<<22;
 
+    /**
+     * The constraints that we want to log to statsd.
+     *
+     * Constraints that can be inferred from other atoms have been excluded to avoid logging too
+     * much information and to reduce redundancy:
+     *
+     * * CONSTRAINT_CHARGING can be inferred with PluggedStateChanged (Atom #32)
+     * * CONSTRAINT_BATTERY_NOT_LOW can be inferred with BatteryLevelChanged (Atom #30)
+     * * CONSTRAINT_CONNECTIVITY can be partially inferred with ConnectivityStateChanged
+     * (Atom #98) and BatterySaverModeStateChanged (Atom #20).
+     * * CONSTRAINT_DEVICE_NOT_DOZING can be mostly inferred with DeviceIdleModeStateChanged
+     * (Atom #21)
+     * * CONSTRAINT_BACKGROUND_NOT_RESTRICTED can be inferred with BatterySaverModeStateChanged
+     * (Atom #20)
+     */
+    private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER
+            | CONSTRAINT_DEADLINE
+            | CONSTRAINT_IDLE
+            | CONSTRAINT_STORAGE_NOT_LOW
+            | CONSTRAINT_TIMING_DELAY
+            | CONSTRAINT_WITHIN_QUOTA;
+
     // Soft override: ignore constraints like time that don't affect API availability
     public static final int OVERRIDE_SOFT = 1;
     // Full override: ignore all constraints including API-affecting like connectivity
@@ -976,6 +1000,12 @@
         }
         satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
         mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
+        if ((STATSD_CONSTRAINTS_TO_LOG & constraint) != 0) {
+            StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED,
+                    sourceUid, null, getBatteryName(), getProtoConstraint(constraint),
+                    state ? StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED
+                            : StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED);
+        }
         return true;
     }
 
@@ -1228,37 +1258,70 @@
         }
     }
 
+    /** Returns a {@link JobServerProtoEnums.Constraint} enum value for the given constraint. */
+    private int getProtoConstraint(int constraint) {
+        switch (constraint) {
+            case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
+                return JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
+            case CONSTRAINT_BATTERY_NOT_LOW:
+                return JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW;
+            case CONSTRAINT_CHARGING:
+                return JobServerProtoEnums.CONSTRAINT_CHARGING;
+            case CONSTRAINT_CONNECTIVITY:
+                return JobServerProtoEnums.CONSTRAINT_CONNECTIVITY;
+            case CONSTRAINT_CONTENT_TRIGGER:
+                return JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER;
+            case CONSTRAINT_DEADLINE:
+                return JobServerProtoEnums.CONSTRAINT_DEADLINE;
+            case CONSTRAINT_DEVICE_NOT_DOZING:
+                return JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING;
+            case CONSTRAINT_IDLE:
+                return JobServerProtoEnums.CONSTRAINT_IDLE;
+            case CONSTRAINT_STORAGE_NOT_LOW:
+                return JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW;
+            case CONSTRAINT_TIMING_DELAY:
+                return JobServerProtoEnums.CONSTRAINT_TIMING_DELAY;
+            case CONSTRAINT_WITHIN_QUOTA:
+                return JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA;
+            default:
+                return JobServerProtoEnums.CONSTRAINT_UNKNOWN;
+        }
+    }
+
     /** 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);
+            proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CHARGING);
         }
         if ((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0) {
-            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_BATTERY_NOT_LOW);
+            proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW);
         }
         if ((constraints & CONSTRAINT_STORAGE_NOT_LOW) != 0) {
-            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_STORAGE_NOT_LOW);
+            proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW);
         }
         if ((constraints & CONSTRAINT_TIMING_DELAY) != 0) {
-            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_TIMING_DELAY);
+            proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_TIMING_DELAY);
         }
         if ((constraints & CONSTRAINT_DEADLINE) != 0) {
-            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_DEADLINE);
+            proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEADLINE);
         }
         if ((constraints & CONSTRAINT_IDLE) != 0) {
-            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_IDLE);
+            proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_IDLE);
         }
         if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) {
-            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CONNECTIVITY);
+            proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONNECTIVITY);
         }
         if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) {
-            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CONTENT_TRIGGER);
+            proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER);
         }
         if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
-            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_DEVICE_NOT_DOZING);
+            proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING);
         }
         if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
-            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_WITHIN_QUOTA);
+            proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA);
+        }
+        if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
+            proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED);
         }
     }