Create statsmanager service

Bug: 146074170
Test: Manual
Change-Id: Ia96f2df24e660fe3293fb2e873e9050a55398cee
diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp
index e6ca544..aed6ad9 100644
--- a/apex/statsd/aidl/Android.bp
+++ b/apex/statsd/aidl/Android.bp
@@ -17,6 +17,18 @@
 // TODO(b/145815909): move StatsDimensionsValue.aidl and StatsLogEventWrapper.aidl here
 filegroup {
     name: "statsd_aidl",
+    srcs: [
+        "android/os/IPullAtomCallback.aidl",
+        "android/os/IPullAtomResultReceiver.aidl",
+        "android/os/IStatsCompanionService.aidl",
+        "android/os/IStatsd.aidl",
+        "android/os/IStatsPullerCallback.aidl",
+        "android/util/StatsEventParcel.aidl",
+    ],
+}
+
+filegroup {
+    name: "statsd_java_aidl",
     srcs: ["**/*.aidl"],
 }
 
diff --git a/apex/statsd/aidl/android/os/IStatsManagerService.aidl b/apex/statsd/aidl/android/os/IStatsManagerService.aidl
new file mode 100644
index 0000000..45ba3a2
--- /dev/null
+++ b/apex/statsd/aidl/android/os/IStatsManagerService.aidl
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2019, 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.
+ */
+
+package android.os;
+
+import android.app.PendingIntent;
+
+/**
+  * Binder interface to communicate with the Java-based statistics service helper.
+  * Contains parcelable objects available only in Java.
+  * {@hide}
+  */
+interface IStatsManagerService {
+
+    /**
+     * Registers the given pending intent for this config key. This intent is invoked when the
+     * memory consumed by the metrics for this configuration approach the pre-defined limits. There
+     * can be at most one listener per config key.
+     *
+     * Requires Manifest.permission.DUMP.
+     */
+    void setDataFetchOperation(long configKey, in PendingIntent pendingIntent,
+        in String packageName);
+
+    /**
+     * Registers the given pending intent for this packagename. This intent is invoked when the
+     * active status of any of the configs sent by this package changes and will contain a list of
+     * config ids that are currently active. It also returns the list of configs that are currently
+     * active. There can be at most one active configs changed listener per package.
+     *
+     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+     */
+    long[] setActiveConfigsChangedOperation(in PendingIntent pendingIntent, in String packageName);
+
+    /**
+     * Set the PendingIntent to be used when broadcasting subscriber
+     * information to the given subscriberId within the given config.
+     *
+     * Suppose that the calling uid has added a config with key configKey, and that in this config
+     * it is specified that when a particular anomaly is detected, a broadcast should be sent to
+     * a BroadcastSubscriber with id subscriberId. This function links the given pendingIntent with
+     * that subscriberId (for that config), so that this pendingIntent is used to send the broadcast
+     * when the anomaly is detected.
+     *
+     * This function can only be called by the owner (uid) of the config. It must be called each
+     * time statsd starts. Later calls overwrite previous calls; only one PendingIntent is stored.
+     *
+     * Requires Manifest.permission.DUMP.
+     */
+    void setBroadcastSubscriber(long configKey, long subscriberId, in PendingIntent pendingIntent,
+                                in String packageName);
+}
\ No newline at end of file
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
new file mode 100644
index 0000000..71b52e2
--- /dev/null
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package com.android.server.stats;
+
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.server.SystemService;
+
+/**
+ * @hide
+ */
+public class StatsCompanion {
+    private static final String TAG = "StatsCompanion";
+    private static final boolean DEBUG = false;
+
+    /**
+     * Lifecycle class for both {@link StatsCompanionService} and {@link StatsManagerService}.
+     */
+    public static final class Lifecycle extends SystemService {
+        private StatsCompanionService mStatsCompanionService;
+        private StatsManagerService mStatsManagerService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mStatsCompanionService = new StatsCompanionService(getContext());
+            mStatsManagerService = new StatsManagerService(getContext());
+            mStatsCompanionService.setStatsManagerService(mStatsManagerService);
+            mStatsManagerService.setStatsCompanionService(mStatsCompanionService);
+
+            try {
+                publishBinderService(Context.STATS_COMPANION_SERVICE,
+                        mStatsCompanionService);
+                if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
+                publishBinderService(Context.STATS_MANAGER_SERVICE,
+                        mStatsManagerService);
+                if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_MANAGER_SERVICE);
+            } catch (Exception e) {
+                Slog.e(TAG, "Failed to publishBinderService", e);
+            }
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            super.onBootPhase(phase);
+            if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+                mStatsCompanionService.systemReady();
+                mStatsManagerService.systemReady();
+            }
+        }
+    }
+}
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index bc7716e..157da63 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -137,7 +137,6 @@
 import com.android.internal.util.DumpUtils;
 import com.android.server.BinderCallsStatsService;
 import com.android.server.LocalServices;
-import com.android.server.SystemService;
 import com.android.server.SystemServiceManager;
 import com.android.server.am.MemoryStatUtil.MemoryStat;
 import com.android.server.notification.NotificationManagerService;
@@ -278,6 +277,8 @@
     private final BroadcastReceiver mUserUpdateReceiver;
     private final ShutdownEventReceiver mShutdownEventReceiver;
 
+    private StatsManagerService mStatsManagerService;
+
     private static final class PullerKey {
         private final int mUid;
         private final int mAtomTag;
@@ -2681,6 +2682,7 @@
             Slog.d(TAG, "learned that statsdReady");
         }
         sayHiToStatsd(); // tell statsd that we're ready too and link to it
+        mStatsManagerService.systemReady();
         mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED)
                         .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
                 UserHandle.SYSTEM, android.Manifest.permission.DUMP);
@@ -2736,7 +2738,7 @@
         }
     }
 
-    // Lifecycle and related code
+    // Statsd related code
 
     /**
      * Fetches the statsd IBinder service.
@@ -2747,42 +2749,18 @@
         return IStatsd.Stub.asInterface(ServiceManager.getService("stats"));
     }
 
-    public static final class Lifecycle extends SystemService {
-        private StatsCompanionService mStatsCompanionService;
-
-        public Lifecycle(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void onStart() {
-            mStatsCompanionService = new StatsCompanionService(getContext());
-            try {
-                publishBinderService(Context.STATS_COMPANION_SERVICE,
-                        mStatsCompanionService);
-                if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
-            } catch (Exception e) {
-                Slog.e(TAG, "Failed to publishBinderService", e);
-            }
-        }
-
-        @Override
-        public void onBootPhase(int phase) {
-            super.onBootPhase(phase);
-            if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
-                mStatsCompanionService.systemReady();
-            }
-        }
-    }
-
     /**
      * Now that the android system is ready, StatsCompanion is ready too, so inform statsd.
      */
-    private void systemReady() {
+    void systemReady() {
         if (DEBUG) Slog.d(TAG, "Learned that systemReady");
         sayHiToStatsd();
     }
 
+    void setStatsManagerService(StatsManagerService statsManagerService) {
+        mStatsManagerService = statsManagerService;
+    }
+
     /**
      * Tells statsd that statscompanion is ready. If the binder call returns, link to
      * statsd.
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
new file mode 100644
index 0000000..f3bf909
--- /dev/null
+++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package com.android.server.stats;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.IStatsManagerService;
+import android.os.IStatsd;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * @hide
+ */
+public class StatsManagerService extends IStatsManagerService.Stub {
+
+    private static final String TAG = "StatsManagerService";
+    private static final boolean DEBUG = false;
+
+    @GuardedBy("sStatsdLock")
+    private static IStatsd sStatsd;
+    private static final Object sStatsdLock = new Object();
+
+    private StatsCompanionService mStatsCompanionService;
+
+    public StatsManagerService(Context context) {
+        super();
+    }
+
+    @Override
+    public void setDataFetchOperation(long configKey, PendingIntent pendingIntent,
+            String packageName) {
+        // no-op
+        if (DEBUG) {
+            Slog.d(TAG, "setDataFetchOperation");
+        }
+    }
+
+    @Override
+    public long[] setActiveConfigsChangedOperation(PendingIntent pendingIntent,
+            String packageName) {
+        // no-op
+        if (DEBUG) {
+            Slog.d(TAG, "setActiveConfigsChangedOperation");
+        }
+        return new long[]{};
+    }
+
+    @Override
+    public void setBroadcastSubscriber(long configKey, long subscriberId,
+            PendingIntent pendingIntent, String packageName) {
+        //no-op
+        if (DEBUG) {
+            Slog.d(TAG, "setBroadcastSubscriber");
+        }
+    }
+
+    void setStatsCompanionService(StatsCompanionService statsCompanionService) {
+        mStatsCompanionService = statsCompanionService;
+    }
+
+    void systemReady() {
+        if (DEBUG) {
+            Slog.d(TAG, "statsdReady");
+        }
+        setupStatsManagerService();
+    }
+
+    private void setupStatsManagerService() {
+        synchronized (sStatsdLock) {
+            if (sStatsd != null) {
+                if (DEBUG) {
+                    Slog.e(TAG, "Trying to fetch statsd, but it was already fetched",
+                            new IllegalStateException(
+                                    "sStatsd is not null when being fetched"));
+                }
+                return;
+            }
+            sStatsd = IStatsd.Stub.asInterface(ServiceManager.getService("stats"));
+            // Assume statsd is ready since this is called form statscompanion, link to statsd.
+            try {
+                sStatsd.asBinder().linkToDeath((IBinder.DeathRecipient) () -> {
+                    sStatsd = null;
+                }, 0);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
+            }
+        }
+    }
+}