Add experiment flag to control binder call stats.

For instance, to enabled detailed tracking locally.
adb shell settings put global binder_calls_stats detailed_tracking=true

Also adds the ability to turn off data collection completely and
changing the sampling interval. Uploading data through westworld can
re-use the same flag once implemented.

Test: Unit tested

Change-Id: I808c9902b8124ab643d9b197703d537da040ae3e
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 490fcc1..3d779d8 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -17,15 +17,20 @@
 package com.android.server;
 
 import android.app.AppGlobals;
+import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
 import android.util.Slog;
 
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BinderCallsStats;
 
 import java.io.FileDescriptor;
@@ -41,18 +46,90 @@
     private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
             = "persist.sys.binder_calls_detailed_tracking";
 
-    public static void start() {
-        BinderCallsStatsService service = new BinderCallsStatsService();
-        ServiceManager.addService("binder_calls_stats", service);
-        boolean detailedTrackingEnabled = SystemProperties.getBoolean(
-                PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false);
+    /** Listens for flag changes. */
+    private static class SettingsObserver extends ContentObserver {
+        private static final String SETTINGS_ENABLED_KEY = "enabled";
+        private static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking";
+        private static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data";
+        private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
 
-        if (detailedTrackingEnabled) {
-            Slog.i(TAG, "Enabled CPU usage tracking for binder calls. Controlled by "
-                    + PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
-                    + " or via dumpsys binder_calls_stats --enable-detailed-tracking");
-            BinderCallsStats.getInstance().setDetailedTracking(true);
+        private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
+        private final Context mContext;
+        private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+        public SettingsObserver(Context context) {
+            super(BackgroundThread.getHandler());
+            mContext = context;
+            context.getContentResolver().registerContentObserver(mUri, false, this,
+                    UserHandle.USER_SYSTEM);
+            // Always kick once to ensure that we match current state
+            onChange();
         }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            if (mUri.equals(uri)) {
+                onChange();
+            }
+        }
+
+        public void onChange() {
+            // Do not overwrite the default set manually.
+            if (!SystemProperties.get(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING).isEmpty()) {
+              return;
+            }
+
+            BinderCallsStats stats = BinderCallsStats.getInstance();
+            try {
+                    mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
+                            Settings.Global.BINDER_CALLS_STATS));
+            } catch (IllegalArgumentException e) {
+                    Slog.e(TAG, "Bad binder call stats settings", e);
+            }
+            stats.setEnabled(
+                    mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT));
+            stats.setDetailedTracking(mParser.getBoolean(
+                    SETTINGS_DETAILED_TRACKING_KEY, BinderCallsStats.DETAILED_TRACKING_DEFAULT));
+            stats.setSamplingInterval(mParser.getInt(
+                    SETTINGS_SAMPLING_INTERVAL_KEY,
+                    BinderCallsStats.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
+        }
+    }
+
+    public static class LifeCycle extends SystemService {
+        private BinderCallsStatsService mService;
+
+        public LifeCycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mService = new BinderCallsStatsService();
+            publishBinderService("binder_calls_stats", mService);
+            boolean detailedTrackingEnabled = SystemProperties.getBoolean(
+                    PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false);
+
+            if (detailedTrackingEnabled) {
+                Slog.i(TAG, "Enabled CPU usage tracking for binder calls. Controlled by "
+                        + PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
+                        + " or via dumpsys binder_calls_stats --enable-detailed-tracking");
+                BinderCallsStats.getInstance().setDetailedTracking(true);
+            }
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
+                mService.systemReady(getContext());
+            }
+        }
+    }
+
+    private SettingsObserver mSettingsObserver;
+
+    public void systemReady(Context context) {
+        mSettingsObserver = new SettingsObserver(context);
     }
 
     public static void reset() {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1f1b3f8..252a1fd 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -722,7 +722,7 @@
 
         // Tracks cpu time spent in binder calls
         traceBeginAndSlog("StartBinderCallsStatsService");
-        BinderCallsStatsService.start();
+        mSystemServiceManager.startService(BinderCallsStatsService.LifeCycle.class);
         traceEnd();
     }