Introduce SystemUI-managed alternative system bars.

If a service component is defined in a new secure setting,
SystemUI will attempt to use that service as the status bar
provider.

Falls back to the existing in-process implementation configured
in the product config if the setting is missing or invalid.

Nothing changes yet from a permission point of view.  Alternative
system bar implementations still require the status bar permission.

Also nothing changes from an api point of view.  Alternative
system bar implementations use the existing IStatusBar interface.

This simply enables testing alternative system bar implementations
installed from other trusted, platform-signed packages.

Known caveat: the setting is stored per user, multi-user changes
will be handled in a future CL.

Change-Id: I0413df185f7e75f77ad2ae1bc3689306d5e6e0fb
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java b/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java
new file mode 100644
index 0000000..847bf96
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2013 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.systemui.statusbar;
+
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+/**
+ * Ensure a single status bar service implementation is running at all times.
+ *
+ * <p>The implementation either comes from a service component running in a remote process (defined
+ * using a secure setting), else falls back to using the in-process implementation according
+ * to the product config.
+ */
+public class SystemBars extends SystemUI implements ServiceMonitor.Callbacks {
+    private static final String TAG = "SystemBars";
+    private static final boolean DEBUG = true;
+    private static final int WAIT_FOR_BARS_TO_DIE = 500;
+
+    // manages the implementation coming from the remote process
+    private ServiceMonitor mServiceMonitor;
+
+    // in-process fallback implementation, per the product config
+    private BaseStatusBar mStatusBar;
+
+    @Override
+    public void start() {
+        if (DEBUG) Log.d(TAG, "start");
+        mServiceMonitor = new ServiceMonitor(TAG, DEBUG,
+                mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);
+        mServiceMonitor.start();  // will call onNoService if no remote service is found
+    }
+
+    @Override
+    public void onNoService() {
+        if (DEBUG) Log.d(TAG, "onNoService");
+        createStatusBarFromConfig();  // fallback to using an in-process implementation
+    }
+
+    @Override
+    public long onServiceStartAttempt() {
+        if (DEBUG) Log.d(TAG, "onServiceStartAttempt mStatusBar="+mStatusBar);
+        if (mStatusBar != null) {
+            // tear down the in-process version, we'll recreate it again if needed
+            mStatusBar.destroy();
+            mStatusBar = null;
+            return WAIT_FOR_BARS_TO_DIE;
+        }
+        return 0;
+    }
+
+    private void createStatusBarFromConfig() {
+        if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
+        final String clsName = mContext.getString(R.string.config_statusBarComponent);
+        if (clsName == null || clsName.length() == 0) {
+            throw andLog("No status bar component configured", null);
+        }
+        Class<?> cls = null;
+        try {
+            cls = mContext.getClassLoader().loadClass(clsName);
+        } catch (Throwable t) {
+            throw andLog("Error loading status bar component: " + clsName, t);
+        }
+        try {
+            mStatusBar = (BaseStatusBar) cls.newInstance();
+        } catch (Throwable t) {
+            throw andLog("Error creating status bar component: " + clsName, t);
+        }
+        mStatusBar.mContext = mContext;
+        mStatusBar.start();
+        if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
+    }
+
+    private RuntimeException andLog(String msg, Throwable t) {
+        Log.w(TAG, msg, t);
+        throw new RuntimeException(msg, t);
+    }
+}