Merge "Add support for pulling and pushing statsd atoms to CarService." into qt-qpr1-dev
diff --git a/car_product/sepolicy/private/carservice_app.te b/car_product/sepolicy/private/carservice_app.te
index bc1d74c..6e65a8f 100644
--- a/car_product/sepolicy/private/carservice_app.te
+++ b/car_product/sepolicy/private/carservice_app.te
@@ -12,6 +12,9 @@
 # Allow Car Service to register/access itself with ServiceManager
 add_service(carservice_app, carservice_service)
 
+# Allow Car Service to register its stats service with ServiceManager
+add_service(carservice_app, carstats_service)
+
 allow carservice_app wifi_service:service_manager find;
 
 # Allow Car Service to access certain system services.
diff --git a/car_product/sepolicy/private/service_contexts b/car_product/sepolicy/private/service_contexts
index 7ac544c..38d994c 100644
--- a/car_product/sepolicy/private/service_contexts
+++ b/car_product/sepolicy/private/service_contexts
@@ -1,2 +1,3 @@
 car_service  u:object_r:carservice_service:s0
+car_stats u:object_r:carstats_service:s0
 com.android.car.procfsinspector u:object_r:procfsinspector_service:s0
diff --git a/car_product/sepolicy/private/statsd.te b/car_product/sepolicy/private/statsd.te
new file mode 100644
index 0000000..1a17418
--- /dev/null
+++ b/car_product/sepolicy/private/statsd.te
@@ -0,0 +1,2 @@
+# Allow statsd to pull atoms from car_stats service
+allow statsd carstats_service:service_manager find;
diff --git a/car_product/sepolicy/public/service.te b/car_product/sepolicy/public/service.te
index 87426f4..c6a2e30 100644
--- a/car_product/sepolicy/public/service.te
+++ b/car_product/sepolicy/public/service.te
@@ -1,2 +1,3 @@
 type carservice_service, app_api_service, service_manager_type;
+type carstats_service, service_manager_type;
 type procfsinspector_service, service_manager_type;
diff --git a/service/src/com/android/car/CarService.java b/service/src/com/android/car/CarService.java
index fdc4995..044c791 100644
--- a/service/src/com/android/car/CarService.java
+++ b/service/src/com/android/car/CarService.java
@@ -99,6 +99,7 @@
         linkToDeath(mVehicle, mVehicleDeathRecipient);
 
         ServiceManager.addService("car_service", mICarImpl);
+        ServiceManager.addService("car_stats", mICarImpl.getStatsService());
         SystemProperties.set("boot.car_service_created", "1");
         super.onCreate();
     }
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index d98b780..d2fed46 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -386,6 +386,10 @@
         }
     }
 
+    CarStatsService getStatsService() {
+        return mCarStatsService;
+    }
+
     public static void assertVehicleHalMockPermission(Context context) {
         assertPermission(context, Car.PERMISSION_MOCK_VEHICLE_HAL);
     }
diff --git a/service/src/com/android/car/stats/CarStatsService.java b/service/src/com/android/car/stats/CarStatsService.java
index db23355..64fbf1f 100644
--- a/service/src/com/android/car/stats/CarStatsService.java
+++ b/service/src/com/android/car/stats/CarStatsService.java
@@ -16,27 +16,36 @@
 
 package com.android.car.stats;
 
+import android.Manifest;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.os.StatsLogEventWrapper;
+import android.os.SystemClock;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.StatsLog;
 
 import com.android.car.stats.VmsClientLog.ConnectionState;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.car.ICarStatsService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.function.Consumer;
 import java.util.function.Function;
 
 /**
- * Implements collection and dumpsys reporting of statistics in CSV format.
+ * Implementation of {@link ICarStatsService}, for reporting pulled atoms via statsd.
+ *
+ * Also implements collection and dumpsys reporting of atoms in CSV format.
  */
-public class CarStatsService {
+public class CarStatsService extends ICarStatsService.Stub {
     private static final boolean DEBUG = false;
     private static final String TAG = "CarStatsService";
     private static final String VMS_CONNECTION_STATS_DUMPSYS_HEADER =
@@ -71,12 +80,14 @@
                     .thenComparingInt(VmsClientStats::getLayerChannel)
                     .thenComparingInt(VmsClientStats::getLayerVersion);
 
+    private final Context mContext;
     private final PackageManager mPackageManager;
 
     @GuardedBy("mVmsClientStats")
     private final Map<Integer, VmsClientLog> mVmsClientStats = new ArrayMap<>();
 
     public CarStatsService(Context context) {
+        mContext = context;
         mPackageManager = context.getPackageManager();
     }
 
@@ -97,9 +108,7 @@
         }
     }
 
-    /**
-     * Dumps metrics in CSV format.
-     */
+    @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         List<String> flags = Arrays.asList(args);
         if (args.length == 0 || flags.contains("--vms-client")) {
@@ -107,6 +116,21 @@
         }
     }
 
+    @Override
+    public StatsLogEventWrapper[] pullData(int tagId) {
+        mContext.enforceCallingPermission(Manifest.permission.DUMP, null);
+        if (tagId != StatsLog.VMS_CLIENT_STATS) {
+            Log.w(TAG, "Unexpected tagId: " + tagId);
+            return null;
+        }
+
+        List<StatsLogEventWrapper> ret = new ArrayList<>();
+        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+        long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
+        pullVmsClientStats(tagId, elapsedNanos, wallClockNanos, ret);
+        return ret.toArray(new StatsLogEventWrapper[0]);
+    }
+
     private void dumpVmsStats(PrintWriter writer) {
         synchronized (mVmsClientStats) {
             writer.println(VMS_CONNECTION_STATS_DUMPSYS_HEADER);
@@ -120,11 +144,38 @@
             writer.println();
 
             writer.println(VMS_CLIENT_STATS_DUMPSYS_HEADER);
+            dumpVmsClientStats(entry -> writer.println(
+                    VMS_CLIENT_STATS_DUMPSYS_FORMAT.apply(entry)));
+        }
+    }
+
+    private void pullVmsClientStats(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        dumpVmsClientStats((entry) -> {
+            StatsLogEventWrapper e =
+                    new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+            e.writeInt(entry.getUid());
+
+            e.writeInt(entry.getLayerType());
+            e.writeInt(entry.getLayerChannel());
+            e.writeInt(entry.getLayerVersion());
+
+            e.writeLong(entry.getTxBytes());
+            e.writeLong(entry.getTxPackets());
+            e.writeLong(entry.getRxBytes());
+            e.writeLong(entry.getRxPackets());
+            e.writeLong(entry.getDroppedBytes());
+            e.writeLong(entry.getDroppedPackets());
+            pulledData.add(e);
+        });
+    }
+
+    private void dumpVmsClientStats(Consumer<VmsClientStats> dumpFn) {
+        synchronized (mVmsClientStats) {
             mVmsClientStats.values().stream()
                     .flatMap(log -> log.getLayerEntries().stream())
                     .sorted(VMS_CLIENT_STATS_ORDER)
-                    .forEachOrdered(entry -> writer.println(
-                            VMS_CLIENT_STATS_DUMPSYS_FORMAT.apply(entry)));
+                    .forEachOrdered(dumpFn);
         }
     }
 }
diff --git a/service/src/com/android/car/stats/VmsClientLog.java b/service/src/com/android/car/stats/VmsClientLog.java
index 506a5fc..0fa2198 100644
--- a/service/src/com/android/car/stats/VmsClientLog.java
+++ b/service/src/com/android/car/stats/VmsClientLog.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.car.vms.VmsLayer;
 import android.util.ArrayMap;
+import android.util.StatsLog;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -36,15 +37,20 @@
      */
     public static class ConnectionState {
         // Attempting to connect to the client
-        public static final int CONNECTING = 0;
+        public static final int CONNECTING =
+                StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__CONNECTING;
         // Client connection established
-        public static final int CONNECTED = 1;
+        public static final int CONNECTED =
+                StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__CONNECTED;
         // Client connection closed unexpectedly
-        public static final int DISCONNECTED = 2;
+        public static final int DISCONNECTED =
+                StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__DISCONNECTED;
         // Client connection closed by VMS
-        public static final int TERMINATED = 3;
+        public static final int TERMINATED =
+                StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__TERMINATED;
         // Error establishing the client connection
-        public static final int CONNECTION_ERROR = 4;
+        public static final int CONNECTION_ERROR =
+                StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__CONNECTION_ERROR;
     }
 
     private final Object mLock = new Object();
@@ -77,6 +83,9 @@
      * @param connectionState New connection state
      */
     public void logConnectionState(int connectionState) {
+        StatsLog.write(StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED,
+                mUid, mPackageName, connectionState);
+
         AtomicLong counter;
         synchronized (mLock) {
             counter = mConnectionStateCounters.computeIfAbsent(connectionState,