diff --git a/play-services-chimera-core/build.gradle b/play-services-chimera-core/build.gradle
new file mode 100644
index 00000000..d70a72c2
--- /dev/null
+++ b/play-services-chimera-core/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * SPDX-FileCopyrightText: 2020, microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'maven-publish'
+apply plugin: 'signing'
+
+dependencies {
+ api project(':play-services-basement')
+ api "androidx.lifecycle:lifecycle-service:$lifecycleVersion"
+
+ implementation "androidx.annotation:annotation:$annotationVersion"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
+}
+
+android {
+ compileSdkVersion androidCompileSdk
+ buildToolsVersion "$androidBuildVersionTools"
+
+ defaultConfig {
+ versionName version
+ minSdkVersion androidMinSdk
+ targetSdkVersion androidTargetSdk
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ compileOptions {
+ sourceCompatibility = 1.8
+ targetCompatibility = 1.8
+ }
+}
+
+apply from: '../gradle/publish-android.gradle'
+
+description = 'microG chimera implementation'
diff --git a/play-services-chimera-core/src/main/AndroidManifest.xml b/play-services-chimera-core/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..ffeb76d6
--- /dev/null
+++ b/play-services-chimera-core/src/main/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
If enabled is true, + * {@link #onStartCommand(Intent, int, int)} will return + * {@link Service#START_REDELIVER_INTENT}, so if this process dies before + * {@link #onHandleIntent(Intent)} returns, the process will be restarted + * and the intent redelivered. If multiple Intents have been sent, only + * the most recent one is guaranteed to be redelivered. + * + *
If enabled is false (the default),
+ * {@link #onStartCommand(Intent, int, int)} will return
+ * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
+ * dies along with it.
+ */
+ public void setIntentRedelivery(boolean redelivery) {
+ this.redelivery = redelivery;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ HandlerThread thread = new HandlerThread("IntentService[" + name + "]");
+ thread.start();
+ serviceLooper = thread.getLooper();
+ serviceHandler = new ServiceHandler(serviceLooper);
+ }
+
+ @Override
+ public void onStart(Intent intent, int startId) {
+ Message msg = this.serviceHandler.obtainMessage();
+ msg.arg1 = startId;
+ msg.obj = intent;
+ this.serviceHandler.sendMessage(msg);
+ }
+
+ /**
+ * You should not override this method for your IntentService. Instead,
+ * override {@link #onHandleIntent}, which the system calls when the IntentService
+ * receives a start request.
+ * @see Service#onStartCommand
+ */
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ onStart(intent, startId);
+ return redelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ serviceLooper.quit();
+ }
+
+ /**
+ * Unless you provide binding for your service, you don't need to implement this
+ * method, because the default implementation returns null.
+ * @see Service#onBind
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ /**
+ * This method is invoked on the worker thread with a request to process.
+ * Only one Intent is processed at a time, but the processing happens on a
+ * worker thread that runs independently from other application logic.
+ * So, if this code takes a long time, it will hold up other requests to
+ * the same IntentService, but it will not hold up anything else.
+ * When all requests have been handled, the IntentService stops itself,
+ * so you should not call {@link #stopSelf}.
+ *
+ * @param intent The value passed to {@link
+ * android.content.Context#startService(Intent)}.
+ * This may be null if the service is being restarted after
+ * its process has gone away; see
+ * {@link Service#onStartCommand}
+ * for details.
+ */
+ public abstract void onHandleIntent(Intent intent);
+}
diff --git a/play-services-chimera-core/src/main/java/com/google/android/chimera/Service.java b/play-services-chimera-core/src/main/java/com/google/android/chimera/Service.java
new file mode 100644
index 00000000..b8ed0e4e
--- /dev/null
+++ b/play-services-chimera-core/src/main/java/com/google/android/chimera/Service.java
@@ -0,0 +1,131 @@
+/*
+ * SPDX-FileCopyrightText: 2021, microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package com.google.android.chimera;
+
+import android.app.Application;
+import android.app.Notification;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.IBinder;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public abstract class Service extends ContextWrapper implements InstanceProvider {
+ public static final int START_CONTINUATION_MASK = 0xf;
+ public static final int START_FLAG_REDELIVERY = 1;
+ public static final int START_FLAG_RETRY = 2;
+ public static final int START_NOT_STICKY = 2;
+ public static final int START_REDELIVER_INTENT = 3;
+ public static final int START_STICKY = 1;
+ public static final int START_STICKY_COMPATIBILITY = 0;
+
+ private android.app.Service containerService;
+ private ProxyCallbacks callbacks;
+
+ public interface ProxyCallbacks {
+ void superOnCreate();
+
+ void superOnDestroy();
+
+ int superOnStartCommand(Intent intent, int flags, int startId);
+
+ void superStopSelf();
+
+ void superStopSelf(int startId);
+
+ boolean superStopSelfResult(int startId);
+ }
+
+ public Service() {
+ super(null);
+ }
+
+ protected void dump(FileDescriptor fs, PrintWriter writer, String[] args) {
+ }
+
+ public final Application getApplication() {
+ return containerService.getApplication();
+ }
+
+ @Override
+ public Object getChimeraImpl() {
+ return this;
+ }
+
+ public android.app.Service getContainerService() {
+ return containerService;
+ }
+
+ public abstract IBinder onBind(Intent intent);
+
+
+ public void onConfigurationChanged(Configuration configuration) {
+ }
+
+ public void onCreate() {
+ this.callbacks.superOnCreate();
+ }
+
+ public void onDestroy() {
+ this.callbacks.superOnDestroy();
+ }
+
+ public void onLowMemory() {
+ }
+
+ public void onRebind(Intent intent) {
+ }
+
+ public void onStart(Intent intent, int startId) {
+ }
+
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ return this.callbacks.superOnStartCommand(intent, flags, startId);
+ }
+
+ public void onTaskRemoved(Intent rootIntent) {
+ }
+
+ public void onTrimMemory(int level) {
+ }
+
+ public boolean onUnbind(Intent intent) {
+ return false;
+ }
+
+ public void publicDump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ dump(fd, writer, args);
+ }
+
+ public void setProxy(android.app.Service service, Context context) {
+ this.containerService = service;
+ this.callbacks = (ProxyCallbacks) service;
+ attachBaseContext(context);
+ }
+
+ public final void startForeground(int id, Notification notification) {
+ this.containerService.startForeground(id, notification);
+ }
+
+ public final void stopForeground(boolean removeNotification) {
+ this.containerService.stopForeground(removeNotification);
+ }
+
+ public final void stopSelf() {
+ this.callbacks.superStopSelf();
+ }
+
+ public final boolean stopSelfResult(int startId) {
+ return this.callbacks.superStopSelfResult(startId);
+ }
+
+ public final void stopSelf(int startId) {
+ this.callbacks.superStopSelf(startId);
+ }
+}
diff --git a/play-services-chimera-core/src/main/kotlin/org/microg/gms/chimera/ServiceLoader.kt b/play-services-chimera-core/src/main/kotlin/org/microg/gms/chimera/ServiceLoader.kt
new file mode 100644
index 00000000..5e3da725
--- /dev/null
+++ b/play-services-chimera-core/src/main/kotlin/org/microg/gms/chimera/ServiceLoader.kt
@@ -0,0 +1,18 @@
+/*
+ * SPDX-FileCopyrightText: 2021, microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.chimera
+
+import android.content.Context
+import com.google.android.chimera.Service
+
+interface ServiceLoader {
+ fun loadService(context: Context): Service
+
+ companion object {
+ inline fun