Re-enable the logging of APP_BREADCRUMB_REPORTED atom

Bug: 78613419
Test: manual, cts, unit tests
Change-Id: I279158c8031eda3ee648053ae6a0d13fde7f1176
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index e823f68..1c0e27f 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -988,6 +988,15 @@
     return Status::ok();
 }
 
+Status StatsService::sendAppBreadcrumbAtom(int32_t label, int32_t state) {
+    // Permission check not necessary as it's meant for applications to write to
+    // statsd.
+    android::util::stats_write(util::APP_BREADCRUMB_REPORTED,
+                               IPCThreadState::self()->getCallingUid(), label,
+                               state);
+    return Status::ok();
+}
+
 void StatsService::binderDied(const wp <IBinder>& who) {
     ALOGW("statscompanion service died");
     StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 67fc770..b3a4776 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -139,6 +139,11 @@
     /** Inform statsCompanion that statsd is ready. */
     virtual void sayHiToStatsCompanion();
 
+    /**
+     * Binder call to get AppBreadcrumbReported atom.
+     */
+    virtual Status sendAppBreadcrumbAtom(int32_t label, int32_t state) override;
+
     /** IBinder::DeathRecipient */
     virtual void binderDied(const wp<IBinder>& who) override;
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index bf0f720..4a53e66 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -247,16 +247,6 @@
             return;
         }
 
-        // Label is 2nd from last field and must be from [0, 15].
-        long appHookLabel = event.GetLong(event.size()-1, &err);
-        if (err != NO_ERROR ) {
-            VLOG("APP_BREADCRUMB_REPORTED had error when parsing the label field");
-            return;
-        } else if (appHookLabel < 0 || appHookLabel > 15) {
-            VLOG("APP_BREADCRUMB_REPORTED does not have valid label %ld", appHookLabel);
-            return;
-        }
-
         // The state must be from 0,3. This part of code must be manually updated.
         long appHookState = event.GetLong(event.size(), &err);
         if (err != NO_ERROR ) {
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 8c256be..124f207 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -152,4 +152,10 @@
      * Requires Manifest.permission.DUMP.
      */
     void unsetBroadcastSubscriber(long configKey, long subscriberId, in String packageName);
+
+    /**
+     * Apps can send an atom via this application breadcrumb with the specified label and state for
+     * this label. This allows building custom metrics and predicates.
+     */
+    void sendAppBreadcrumbAtom(int label, int state);
 }
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index e8b4197..e3de307 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -16,59 +16,101 @@
 
 package android.util;
 
-import android.os.Process;
+import android.os.IStatsManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 
 /**
  * StatsLog provides an API for developers to send events to statsd. The events can be used to
  * define custom metrics inside statsd.
  */
 public final class StatsLog extends StatsLogInternal {
-    private static final String TAG = "StatsManager";
+    private static final String TAG = "StatsLog";
+    private static final boolean DEBUG = false;
+
+    private static IStatsManager sService;
 
     private StatsLog() {}
 
     /**
      * Logs a start event.
      *
-     * @param label developer-chosen label that is from [0, 16).
+     * @param label developer-chosen label.
      * @return True if the log request was sent to statsd.
      */
     public static boolean logStart(int label) {
-        if (label >= 0 && label < 16) {
-            StatsLog.write(APP_BREADCRUMB_REPORTED, Process.myUid(),
-                    label, APP_BREADCRUMB_REPORTED__STATE__START);
-            return true;
+        synchronized (StatsLog.class) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging start");
+                    return false;
+                }
+                service.sendAppBreadcrumbAtom(label,
+                        StatsLog.APP_BREADCRUMB_REPORTED__STATE__START);
+                return true;
+            } catch (RemoteException e) {
+                sService = null;
+                if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging start");
+                return false;
+            }
         }
-        return false;
     }
 
     /**
      * Logs a stop event.
      *
-     * @param label developer-chosen label that is from [0, 16).
+     * @param label developer-chosen label.
      * @return True if the log request was sent to statsd.
      */
     public static boolean logStop(int label) {
-        if (label >= 0 && label < 16) {
-            StatsLog.write(APP_BREADCRUMB_REPORTED, Process.myUid(),
-                    label, APP_BREADCRUMB_REPORTED__STATE__STOP);
-            return true;
+        synchronized (StatsLog.class) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging stop");
+                    return false;
+                }
+                service.sendAppBreadcrumbAtom(label, StatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP);
+                return true;
+            } catch (RemoteException e) {
+                sService = null;
+                if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging stop");
+                return false;
+            }
         }
-        return false;
     }
 
     /**
      * Logs an event that does not represent a start or stop boundary.
      *
-     * @param label developer-chosen label that is from [0, 16).
+     * @param label developer-chosen label.
      * @return True if the log request was sent to statsd.
      */
     public static boolean logEvent(int label) {
-        if (label >= 0 && label < 16) {
-            StatsLog.write(APP_BREADCRUMB_REPORTED, Process.myUid(), label,
-                    APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED);
-            return true;
+        synchronized (StatsLog.class) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging event");
+                    return false;
+                }
+                service.sendAppBreadcrumbAtom(
+                        label, StatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED);
+                return true;
+            } catch (RemoteException e) {
+                sService = null;
+                if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging event");
+                return false;
+            }
         }
-        return false;
+    }
+
+    private static IStatsManager getIStatsManagerLocked() throws RemoteException {
+        if (sService != null) {
+            return sService;
+        }
+        sService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
+        return sService;
     }
 }