OmniLib: Create OmniRomSystemServer to handle our platform init.

Change-Id: I7b62978e2a284a3a1ec1ffc1d8c72545d7b0c939
diff --git a/Android.bp b/Android.bp
index 1784ff6..3eddf86 100644
--- a/Android.bp
+++ b/Android.bp
@@ -61,5 +61,6 @@
     libs: [
         "framework",
         "OmniPreference",
+        "services",
     ],
 }
diff --git a/res/values/config.xml b/res/values/config.xml
index 496982d..f1e549f 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -20,4 +20,12 @@
     <bool name="config_FastChargingLedSupported">false</bool>
     <!-- Default value for led color when fast charging is active -->
     <integer name="config_notificationsFastBatteryARGB">0x0000FF</integer>
+
+    <!-- Defines external services to be started by the OmniRomSystemServer at boot. The service itself
+         should publish as a binder services in its onStart -->
+    <string-array name="config_externalOmniRomServices">
+    </string-array>
+
+    <!-- The LineageSystemServer class that is invoked from Android's SystemServer -->
+    <string name="config_externalSystemServer" translatable="false">org.omnirom.omnilib.internal.OmniRomSystemServer</string>
 </resources>
diff --git a/res/values/symbols.xml b/res/values/symbols.xml
index 3537b6d..00e8cc7 100644
--- a/res/values/symbols.xml
+++ b/res/values/symbols.xml
@@ -45,4 +45,9 @@
     <java-symbol name="reboot_system_message" type="string" />
     <java-symbol type="array" name="config_rebootActionsList" />
 
+    <!-- External OmniRom specific core services -->
+    <java-symbol type="array" name="config_externalOmniRomServices" />
+    <!-- OmniRom system server -->
+    <java-symbol type="string" name="config_externalSystemServer" />
+
 </resources>
diff --git a/src/org/omnirom/omnilib/internal/OmniRomSystemServer.java b/src/org/omnirom/omnilib/internal/OmniRomSystemServer.java
new file mode 100644
index 0000000..16368e4
--- /dev/null
+++ b/src/org/omnirom/omnilib/internal/OmniRomSystemServer.java
@@ -0,0 +1,90 @@
+/*
+ * SPDX-FileCopyrightText: 2016 The CyanogenMod Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.omnirom.omnilib.internal;
+
+import android.content.Context;
+import android.os.SystemProperties;
+import android.util.Slog;
+import com.android.server.LocalServices;
+import com.android.server.SystemServiceManager;
+
+import org.omnirom.omnilib.internal.common.OmniRomSystemServiceHelper;
+
+/**
+ * Base OmniRom System Server which handles the starting and states of various OmniRom
+ * specific system services. Since its part of the main looper provided by the system
+ * server, it will be available indefinitely (until all the things die).
+ */
+public class OmniRomSystemServer {
+    private static final String TAG = "OmniRomSystemServer";
+    private Context mSystemContext;
+    private OmniRomSystemServiceHelper mSystemServiceHelper;
+
+    private static final String ENCRYPTING_STATE = "trigger_restart_min_framework";
+    private static final String ENCRYPTED_STATE = "1";
+
+    public OmniRomSystemServer(Context systemContext) {
+        mSystemContext = systemContext;
+        mSystemServiceHelper = new OmniRomSystemServiceHelper(mSystemContext);
+    }
+
+    public static boolean coreAppsOnly() {
+        // Only run "core" apps+services if we're encrypting the device.
+        final String cryptState = SystemProperties.get("vold.decrypt");
+        final boolean isAlarmBoot = SystemProperties.getBoolean("ro.alarm_boot", false);
+        return ENCRYPTING_STATE.equals(cryptState) ||
+               ENCRYPTED_STATE.equals(cryptState) ||
+               isAlarmBoot;
+    }
+
+    /**
+     * Invoked via reflection by the SystemServer
+     */
+    private void run() {
+        // Start services.
+        try {
+            startServices();
+        } catch (Throwable ex) {
+            Slog.e("System", "******************************************");
+            Slog.e("System", "************ Failure starting omnirom system services", ex);
+            throw ex;
+        }
+    }
+
+    private void startServices() {
+        final Context context = mSystemContext;
+        final SystemServiceManager ssm = LocalServices.getService(SystemServiceManager.class);
+        String[] externalServices = context.getResources().getStringArray(
+                org.omnirom.omnilib.R.array.config_externalOmniRomServices);
+
+        for (String service : externalServices) {
+            try {
+                Slog.i(TAG, "Attempting to start service " + service);
+                OmniRomSystemService omniromSystemService =  mSystemServiceHelper.getServiceFor(service);
+                if (context.getPackageManager().hasSystemFeature(
+                        omniromSystemService.getFeatureDeclaration())) {
+                    if (coreAppsOnly() && !omniromSystemService.isCoreService()) {
+                        Slog.d(TAG, "Not starting " + service +
+                                " - only parsing core apps");
+                    } else {
+                        Slog.i(TAG, "Starting service " + service);
+                        ssm.startService(omniromSystemService.getClass());
+                    }
+                } else {
+                    Slog.i(TAG, "Not starting service " + service +
+                            " due to feature not declared on device");
+                }
+            } catch (Throwable e) {
+                reportWtf("starting " + service , e);
+            }
+        }
+    }
+
+    private void reportWtf(String msg, Throwable e) {
+        Slog.w(TAG, "***********************************************");
+        Slog.wtf(TAG, "BOOT FAILURE " + msg, e);
+    }
+}
diff --git a/src/org/omnirom/omnilib/internal/OmniRomSystemService.java b/src/org/omnirom/omnilib/internal/OmniRomSystemService.java
new file mode 100644
index 0000000..3426733
--- /dev/null
+++ b/src/org/omnirom/omnilib/internal/OmniRomSystemService.java
@@ -0,0 +1,26 @@
+/*
+ * SPDX-FileCopyrightText: 2016 The CyanogenMod Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.omnirom.omnilib.internal;
+
+import android.content.Context;
+import com.android.server.SystemService;
+
+public abstract class OmniRomSystemService extends SystemService {
+    public OmniRomSystemService(Context context) {
+        super(context);
+    }
+
+    public abstract String getFeatureDeclaration();
+
+
+    /**
+     * Override and return true if the service should be started
+     * before the device is decrypted.
+     */
+    public boolean isCoreService() {
+        return true;
+    }
+}
diff --git a/src/org/omnirom/omnilib/internal/common/OmniRomSystemServiceHelper.java b/src/org/omnirom/omnilib/internal/common/OmniRomSystemServiceHelper.java
new file mode 100644
index 0000000..fc32326
--- /dev/null
+++ b/src/org/omnirom/omnilib/internal/common/OmniRomSystemServiceHelper.java
@@ -0,0 +1,56 @@
+/*
+ * SPDX-FileCopyrightText: 2016 The CyanogenMod Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.omnirom.omnilib.internal.common;
+
+import android.content.Context;
+import org.omnirom.omnilib.internal.OmniRomSystemService;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Helper methods for fetching a OmniRomSystemService from a class declaration
+ */
+public class OmniRomSystemServiceHelper {
+    private Context mContext;
+
+    public OmniRomSystemServiceHelper(Context context) {
+        mContext = context;
+    }
+
+    public OmniRomSystemService getServiceFor(String className) {
+        final Class<OmniRomSystemService> serviceClass;
+        try {
+            serviceClass = (Class<OmniRomSystemService>)Class.forName(className);
+        } catch (ClassNotFoundException ex) {
+            throw new RuntimeException("Failed to create service " + className
+                    + ": service class not found", ex);
+        }
+
+        return getServiceFromClass(serviceClass);
+    }
+
+    public <T extends OmniRomSystemService> T getServiceFromClass(Class<T> serviceClass) {
+        final T service;
+        try {
+            Constructor<T> constructor = serviceClass.getConstructor(Context.class);
+            service = constructor.newInstance(mContext);
+        } catch (InstantiationException ex) {
+            throw new RuntimeException("Failed to create service " + serviceClass
+                    + ": service could not be instantiated", ex);
+        } catch (IllegalAccessException ex) {
+            throw new RuntimeException("Failed to create service " + serviceClass
+                    + ": service must have a public constructor with a Context argument", ex);
+        } catch (NoSuchMethodException ex) {
+            throw new RuntimeException("Failed to create service " + serviceClass
+                    + ": service must have a public constructor with a Context argument", ex);
+        } catch (InvocationTargetException ex) {
+            throw new RuntimeException("Failed to create service " + serviceClass
+                    + ": service constructor threw an exception", ex);
+        }
+        return service;
+    }
+}