Merge "Statsd can pull kernel wakelock data"
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index e2e1a7f..56f9512 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -44,6 +44,7 @@
     ../../core/java/android/os/IStatsManager.aidl \
     src/StatsService.cpp \
     src/AnomalyMonitor.cpp \
+    src/StatsPuller.cpp \
     src/LogEntryPrinter.cpp \
     src/LogReader.cpp \
     src/main.cpp \
diff --git a/cmds/statsd/src/StatsPuller.cpp b/cmds/statsd/src/StatsPuller.cpp
new file mode 100644
index 0000000..94e8361
--- /dev/null
+++ b/cmds/statsd/src/StatsPuller.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "StatsPuller"
+#define DEBUG true
+
+#include "StatsPuller.h"
+#include "StatsService.h"
+#include <android/os/IStatsCompanionService.h>
+#include <cutils/log.h>
+
+using namespace android;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+String16 StatsPuller::pull(int pullCode) {
+    if (DEBUG) ALOGD("Initiating pulling %d", pullCode);
+
+    switch (pullCode) {
+        // All stats_companion_service cases go here with fallthroughs
+        case PULL_CODE_KERNEL_WAKELOCKS: {
+            // TODO: Consider caching the statsCompanion service
+            sp <IStatsCompanionService>
+                    statsCompanion = StatsService::getStatsCompanionService();
+            String16 returned_value("");
+            Status status = statsCompanion->pullData(pullCode, &returned_value);
+            if (DEBUG) ALOGD("Finished pulling the data");
+            if (!status.isOk()) {
+                ALOGW("error pulling data of type %d", pullCode);
+            }
+            return returned_value;
+        }
+
+        // case OTHER_TYPES: etc.
+
+        default: {
+            ALOGE("invalid pull code %d", pullCode);
+            return String16("");
+        }
+    }
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/StatsPuller.h b/cmds/statsd/src/StatsPuller.h
new file mode 100644
index 0000000..05343b5
--- /dev/null
+++ b/cmds/statsd/src/StatsPuller.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef STATSD_STATSPULLER_H
+#define STATSD_STATSPULLER_H
+
+#include <utils/String16.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StatsPuller {
+public:
+    // Enums of pulled data types (pullCodes)
+    // These values must be kept in sync with com/android/server/stats/StatsCompanionService.java.
+    // TODO: pull the constant from stats_events.proto instead
+    const static int PULL_CODE_KERNEL_WAKELOCKS = 20;
+
+    StatsPuller();
+    ~StatsPuller();
+
+    static String16 pull(int pullCode);
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+#endif //STATSD_STATSPULLER_H
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index a28f085..ae7d66b 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -177,6 +177,9 @@
 
     if (DEBUG) ALOGD("StatsService::informPollAlarmFired succeeded");
     // TODO: determine what services to poll and poll (or ask StatsCompanionService to poll) them.
+    String16 output = StatsPuller::pull(StatsPuller::PULL_CODE_KERNEL_WAKELOCKS);
+    // TODO: do something useful with the output instead of writing a string to screen.
+    ALOGD("%s", String8(output).string());
 
     return Status::ok();
 }
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index eb6aa49..a16b115 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -19,6 +19,7 @@
 
 #include "AnomalyMonitor.h"
 #include "StatsLogProcessor.h"
+#include "StatsPuller.h"
 
 #include <android/os/BnStatsManager.h>
 #include <android/os/IStatsCompanionService.h>
@@ -66,19 +67,21 @@
     /** Inform statsCompanion that statsd is ready. */
     virtual void sayHiToStatsCompanion();
 
-private:
-    sp<StatsLogProcessor> m_processor;  // Reference to the processor for updating configs.
+    // TODO: Move this to a more logical file/class
+    // TODO: Should be private. Temporarily public for testing purposes only.
+    const sp<AnomalyMonitor> mAnomalyMonitor;
 
-    const sp<AnomalyMonitor> mAnomalyMonitor;  // TODO: Move this to a more logical file/class
+    /** Fetches and returns the StatsCompanionService. */
+    static sp<IStatsCompanionService> getStatsCompanionService();
+
+ private:
+    sp<StatsLogProcessor> m_processor;  // Reference to the processor for updating configs.
 
     status_t doPrintStatsLog(FILE* out, const Vector<String8>& args);
 
     void printCmdHelp(FILE* out);
 
     status_t doLoadConfig(FILE* in);
-
-    /** Fetches the StatsCompanionService. */
-    sp<IStatsCompanionService> getStatsCompanionService();
 };
 
 // --- StatsdDeathRecipient ---
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index a83d313..d8f9567 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -20,11 +20,11 @@
   * Binder interface to communicate with the Java-based statistics service helper.
   * {@hide}
   */
-oneway interface IStatsCompanionService {
+interface IStatsCompanionService {
     /**
      * Tell statscompanion that stastd is up and running.
      */
-    void statsdReady();
+    oneway void statsdReady();
 
     /**
     * Register an alarm for anomaly detection to fire at the given timestamp (ms since epoch).
@@ -32,10 +32,10 @@
     * Uses AlarmManager.set API, so  if the timestamp is in the past, alarm fires immediately, and
     * alarm is inexact.
     */
-    void setAnomalyAlarm(long timestampMs);
+    oneway void setAnomalyAlarm(long timestampMs);
 
     /** Cancel any anomaly detection alarm. */
-    void cancelAnomalyAlarm();
+    oneway void cancelAnomalyAlarm();
 
     /**
       * Register a repeating alarm for polling to fire at the given timestamp and every
@@ -44,8 +44,11 @@
       * Uses AlarmManager.setRepeating API, so if the timestamp is in past, alarm fires immediately,
       * and alarm is inexact.
       */
-    void setPollingAlarms(long timestampMs, long intervalMs);
+    oneway void setPollingAlarms(long timestampMs, long intervalMs);
 
     /** Cancel any repeating polling alarm. */
-    void cancelPollingAlarms();
+    oneway void cancelPollingAlarms();
+
+    /** Pull the specified data. Results will be sent to statsd when complete. */
+    String pullData(int pullCode);
 }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 1a62662..05fd248 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -30,8 +30,12 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.KernelWakelockReader;
+import com.android.internal.os.KernelWakelockStats;
 import com.android.server.SystemService;
 
+import java.util.Map;
+
 /**
  * Helper service for statsd (the native stats management service in cmds/statsd/).
  * Used for registering and receiving alarms on behalf of statsd.
@@ -157,7 +161,44 @@
         }
     }
 
-    @Override
+    // These values must be kept in sync with cmd/statsd/StatsPuller.h.
+    // TODO: pull the constant from stats_events.proto instead
+    private static final int PULL_CODE_KERNEL_WAKELOCKS = 20;
+
+    private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
+    private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
+
+    @Override // Binder call
+    public String pullData(int pullCode) {
+        enforceCallingPermission();
+        if (DEBUG) Slog.d(TAG, "Fetching " + pullCode);
+
+        StringBuilder s = new StringBuilder(); // TODO: use and return a Parcel instead of a string
+        switch (pullCode) {
+            case PULL_CODE_KERNEL_WAKELOCKS:
+                final KernelWakelockStats wakelockStats =
+                        mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
+
+                for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
+                    String name = ent.getKey();
+                    KernelWakelockStats.Entry kws = ent.getValue();
+                    s.append("Wakelock ")
+                            .append(name)
+                            .append(", time=")
+                            .append(kws.mTotalTime)
+                            .append(", count=")
+                            .append(kws.mCount)
+                            .append('\n');
+                }
+                break;
+            default:
+                Slog.w(TAG, "No such pollable data as " + pullCode);
+                return null;
+        }
+        return s.toString();
+    }
+
+    @Override // Binder call
     public void statsdReady() {
         enforceCallingPermission();
         if (DEBUG) Slog.d(TAG, "learned that statsdReady");