Repo Created

This commit is contained in:
Fr4nz D13trich 2025-11-15 17:44:12 +01:00
parent eb305e2886
commit a8c22c65db
4784 changed files with 329907 additions and 2 deletions

View file

@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
apply plugin: 'kotlin-android'
apply plugin: 'signing'
android {
namespace "org.microg.gms.droidguard"
compileSdkVersion androidCompileSdk
buildToolsVersion "$androidBuildVersionTools"
buildFeatures {
aidl = true
}
defaultConfig {
versionName version
minSdkVersion androidMinSdk
targetSdkVersion androidTargetSdk
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
kotlinOptions {
jvmTarget = 1.8
}
}
apply from: '../gradle/publish-android.gradle'
description = 'microG implementation of play-services-droidguard'
dependencies {
api project(':play-services-base')
implementation "androidx.annotation:annotation:$annotationVersion"
}

View file

@ -0,0 +1,72 @@
/*
* SPDX-FileCopyrightText: 2021, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
apply plugin: 'com.android.library'
apply plugin: 'com.squareup.wire'
apply plugin: 'kotlin-android'
apply plugin: 'maven-publish'
apply plugin: 'signing'
dependencies {
api project(':play-services-droidguard')
implementation project(':play-services-base-core')
implementation project(':play-services-chimera-core')
implementation project(':play-services-tasks-ktx')
implementation "androidx.appcompat:appcompat:$appcompatVersion"
implementation "androidx.core:core-ktx:$coreVersion"
implementation "androidx.preference:preference-ktx:$preferenceVersion"
implementation "com.android.volley:volley:$volleyVersion"
implementation "com.squareup.wire:wire-runtime:$wireVersion"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
}
wire {
kotlin {}
}
android {
namespace "org.microg.gms.droidguard.core"
compileSdkVersion androidCompileSdk
buildToolsVersion "$androidBuildVersionTools"
buildFeatures {
buildConfig = true
}
defaultConfig {
versionName "20.47.14"
versionCode 204714000
minSdkVersion androidMinSdk
targetSdkVersion androidTargetSdk
buildConfigField("String", "VERSION_NAME", "\"${defaultConfig.versionName}\"")
buildConfigField("int", "VERSION_CODE", "${defaultConfig.versionCode}")
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'MissingTranslation', 'GetLocales'
}
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
kotlinOptions {
jvmTarget = 1.8
}
}
apply from: '../../gradle/publish-android.gradle'
description = 'microG service implementation for play-services-droidguard'

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ SPDX-FileCopyrightText: 2021, microG Project Team
~ SPDX-License-Identifier: Apache-2.0
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application>
<service
android:name=".DroidGuardService"
android:enabled="true"
android:exported="true"
android:process="com.google.android.gms.unstable">
<intent-filter>
<action android:name="com.google.android.gms.droidguard.service.INIT" />
<action android:name="com.google.android.gms.droidguard.service.PING" />
<action android:name="com.google.android.gms.droidguard.service.START" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
</manifest>

View file

@ -0,0 +1,145 @@
/*
* SPDX-FileCopyrightText: 2021, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.droidguard;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.util.Base64;
import android.util.Log;
import androidx.annotation.Nullable;
import com.google.android.gms.framework.tracing.wrapper.TracingIntentService;
import org.microg.gms.droidguard.core.DroidGuardPreferences;
import org.microg.gms.droidguard.core.DroidGuardServiceBroker;
import org.microg.gms.droidguard.GuardCallback;
import org.microg.gms.droidguard.core.NetworkHandleProxyFactory;
import org.microg.gms.droidguard.PingData;
import org.microg.gms.droidguard.Request;
import org.microg.gms.droidguard.core.HardwareAttestationBlockingProvider;
import java.util.Collections;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class DroidGuardChimeraService extends TracingIntentService {
public static final Object a = new Object();
// factory
public NetworkHandleProxyFactory b;
// widevine
public Object c;
// executor
public Executor d;
// log
public Object e;
private static final Object f = new Object();
// ping
private Object g;
// handler
private Handler h;
public DroidGuardChimeraService() {
super("DG");
setIntentRedelivery(true);
}
public DroidGuardChimeraService(NetworkHandleProxyFactory factory, Object ping, Object database) {
super("DG");
setIntentRedelivery(true);
this.b = factory;
this.g = ping;
this.h = new Handler();
}
// fsc
private final void c(byte[] data) {
PingData ping = null;
if (data != null) {
Log.d("GmsGuardChimera", "c(" + Base64.encodeToString(data, Base64.NO_WRAP) + ")", new RuntimeException().fillInStackTrace());
try {
ping = PingData.ADAPTER.decode(data);
} catch (Exception e) {
Log.w("GmsGuardChimera", e);
}
} else {
Log.d("GmsGuardChimera", "c(null)", new RuntimeException().fillInStackTrace());
}
try {
byte[] bytes = b.createPingHandle(getPackageName(), "full", b(""), ping).run(Collections.emptyMap());
Log.d("GmsGuardChimera", "c.bytes = " + Base64.encodeToString(bytes, Base64.NO_WRAP));
Request fastRequest = b.createRequest("fast", getPackageName(), null, bytes);
b.fetchFromServer("fast", fastRequest);
} catch (Exception e) {
Log.w("GmsGuardChimera", e);
}
}
// handle intent
public final void a(@Nullable Intent intent) {
Log.d("GmsGuardChimera", "a(" + intent + ")");
if (intent != null && intent.getAction() != null && intent.getAction().equals("com.google.android.gms.droidguard.service.PING")) {
byte[] byteData = intent.getByteArrayExtra("data");
if (byteData == null) {
int[] intData = intent.getIntArrayExtra("data");
if (intData == null) {
c(null);
return;
}
byteData = new byte[intData.length];
for (int i = 0; i < intData.length; i++) {
byteData[i] = (byte) intData[i];
}
}
c(byteData);
}
}
// getCallback
public final GuardCallback b(String packageName) {
Log.d("GmsGuardChimera", "b[getCallback](" + packageName + ")");
return new GuardCallback(this, packageName);
}
@Nullable
@Override
public final IBinder onBind(Intent intent) {
if (intent != null && intent.getAction() != null && intent.getAction().equals("com.google.android.gms.droidguard.service.START")) {
HardwareAttestationBlockingProvider.ensureEnabled(DroidGuardPreferences.isHardwareAttestationBlocked(this));
return new DroidGuardServiceBroker(this);
}
return null;
}
@Override
public void onCreate() {
this.e = new Object();
this.b = new NetworkHandleProxyFactory(this);
this.g = new Object();
this.h = new Handler();
this.c = new Object();
this.d = new ThreadPoolExecutor(1, 1, 0, TimeUnit.NANOSECONDS, new LinkedBlockingQueue<>(1), new ThreadPoolExecutor.DiscardPolicy());
HardwareAttestationBlockingProvider.ensureEnabled(DroidGuardPreferences.isHardwareAttestationBlocked(this));
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
this.e = null;
this.b = null;
this.g = null;
this.h = null;
this.c = null;
this.d = null;
}
}

View file

@ -0,0 +1,57 @@
/*
* SPDX-FileCopyrightText: 2021, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.framework.tracing.wrapper;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.chimera.IntentService;
import org.microg.gms.utils.PackageManagerWrapper;
import org.microg.gms.droidguard.core.VersionUtil;
public abstract class TracingIntentService extends IntentService {
private static final String TAG = "TracingIntentService";
public TracingIntentService(String name) {
super(name);
}
@Override
public void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
}
protected abstract void a(@Nullable Intent intent);
@Override
public PackageManager getPackageManager() {
return new PackageManagerWrapper(super.getPackageManager()) {
@NonNull
@Override
public PackageInfo getPackageInfo(@NonNull String packageName, int flags) {
PackageInfo packageInfo = super.getPackageInfo(packageName, flags);
if ("com.google.android.gms".equals(packageName)) {
VersionUtil versionUtil = new VersionUtil(TracingIntentService.this);
packageInfo.versionCode = versionUtil.getVersionCode();
packageInfo.versionName = versionUtil.getVersionString();
packageInfo.sharedUserId = "com.google.uid.shared";
}
return packageInfo;
}
};
}
@Override
public void onHandleIntent(@Nullable Intent intent) {
this.a(intent);
}
}

View file

@ -0,0 +1,80 @@
/*
* SPDX-FileCopyrightText: 2021, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard;
import android.content.Context;
import android.media.MediaDrm;
import android.util.Log;
import org.microg.gms.droidguard.core.FallbackCreator;
import org.microg.gms.settings.SettingsContract;
import java.util.HashMap;
import static android.os.Build.VERSION.SDK_INT;
/**
* Callbacks invoked from the DroidGuard VM
* <p>
* We keep this file in Java to ensure ABI compatibility.
* Methods are invoked by name from within the VM and thus must keep current name.
*/
public class GuardCallback {
private static final String TAG = "GmsGuardCallback";
private final Context context;
private final String packageName;
public GuardCallback(Context context, String packageName) {
this.context = context;
this.packageName = packageName;
}
public final String a(final byte[] array) {
Log.d(TAG, "a[?](" + array + ")");
return new String(FallbackCreator.create(new HashMap<>(), array, "", context, null));
}
// getAndroidId
public final String b() {
try {
long androidId = SettingsContract.INSTANCE.getSettings(context, SettingsContract.CheckIn.INSTANCE.getContentUri(context), new String[]{SettingsContract.CheckIn.ANDROID_ID}, cursor -> cursor.getLong(0));
Log.d(TAG, "b[getAndroidId]() = " + androidId);
return String.valueOf(androidId);
} catch (Throwable e) {
Log.w(TAG, "Failed to get Android ID, fallback to random", e);
}
long androidId = (long) (Math.random() * Long.MAX_VALUE);
Log.d(TAG, "b[getAndroidId]() = " + androidId + " (random)");
return String.valueOf(androidId);
}
// getPackageName
public final String c() {
Log.d(TAG, "c[getPackageName]() = " + packageName);
return packageName;
}
// closeMediaDrmSession
public final void d(final Object mediaDrm, final byte[] sessionId) {
Log.d(TAG, "d[closeMediaDrmSession](" + mediaDrm + ", " + sessionId + ")");
synchronized (MediaDrmLock.LOCK) {
if (SDK_INT >= 18) {
((MediaDrm) mediaDrm).closeSession(sessionId);
}
}
}
public final void e(final int task) {
Log.d(TAG, "e[?](" + task + ")");
// TODO: Open database
if (task == 1) {
// TODO
} else if (task == 0) {
// TODO
}
// TODO: Set value in database
}
}

View file

@ -0,0 +1,10 @@
/*
* SPDX-FileCopyrightText: 2021, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard;
public class MediaDrmLock {
public static final Object LOCK = new Object();
}

View file

@ -0,0 +1,71 @@
/*
* SPDX-FileCopyrightText: 2021, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core
import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
/**
* - a: id
* - b: timestamp
* - c: seconds until expiry
* - d: vm key
* - e: ?
* - f: byte code
* - g: extra
*/
class DgDatabaseHelper(context: Context) : SQLiteOpenHelper(context, "dg.db", null, 2) {
override fun onCreate(db: SQLiteDatabase) {
// Note: "NON NULL" is actually not a valid sqlite constraint, but this is what we see in the original database 🤷
db.execSQL("CREATE TABLE main (a TEXT NOT NULL, b LONG NOT NULL, c LONG NOT NULL, d TEXT NON NULL, e TEXT NON NULL,f BLOB NOT NULL,g BLOB NOT NULL);");
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("DROP TABLE IF EXISTS main;");
this.onCreate(db);
}
/**
* @return vm key, byte code, extra
*/
fun get(id: String): Triple<String, ByteArray, ByteArray>? = readableDatabase.use { db ->
val time = System.currentTimeMillis() / 1000
val it = db.query("main", arrayOf("f", "d", "e", "c", "g"), "a = ? AND b <= $time AND $time < (b + c)", arrayOf(id), null, null, "b DESC", "1")
try {
if (it.moveToNext()) {
Triple(it.getString(1), it.getBlob(0), it.getBlob(4))
} else {
null
}
} finally {
it.close()
}
}
fun put(id: String, expiry: Long, vmKey: String, byteCode: ByteArray, extra: ByteArray) {
val dbData = ContentValues().apply {
put("a", id)
put("b", System.currentTimeMillis() / 1000)
put("c", expiry)
put("d", vmKey)
put("e", "")
put("f", byteCode)
put("g", extra)
}
writableDatabase.use {
it.beginTransaction()
if (expiry <= 0) {
it.delete("main", "a = ?", arrayOf(id))
} else if (it.update("main", dbData, "a = ?", arrayOf(id)) <= 0) {
it.insert("main", null, dbData)
}
it.setTransactionSuccessful()
it.endTransaction()
}
}
}

View file

@ -0,0 +1,19 @@
/*
* SPDX-FileCopyrightText: 2021, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
class DgpDatabaseHelper(context: Context) : SQLiteOpenHelper(context, "dgp.db", null, 1) {
override fun onCreate(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE t (a BLOB NOT NULL);");
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
}
}

View file

@ -0,0 +1,118 @@
/*
* SPDX-FileCopyrightText: 2021 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core
import android.annotation.SuppressLint
import android.content.Context
import android.os.ConditionVariable
import android.os.ParcelFileDescriptor
import android.os.Parcelable
import android.util.Log
import com.google.android.gms.droidguard.internal.DroidGuardInitReply
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
import com.google.android.gms.droidguard.internal.IDroidGuardHandle
import org.microg.gms.droidguard.BytesException
import org.microg.gms.droidguard.GuardCallback
import org.microg.gms.droidguard.HandleProxy
import java.io.FileNotFoundException
class DroidGuardHandleImpl(private val context: Context, private val packageName: String, private val factory: NetworkHandleProxyFactory, private val callback: GuardCallback) : IDroidGuardHandle.Stub() {
private val condition = ConditionVariable()
private var flow: String? = null
private var handleProxy: HandleProxy? = null
private var handleInitError: Throwable? = null
override fun init(flow: String?) {
Log.d(TAG, "init($flow)")
initWithRequest(flow, null)
}
@SuppressLint("SetWorldReadable")
override fun initWithRequest(flow: String?, request: DroidGuardResultsRequest?): DroidGuardInitReply {
Log.d(TAG, "initWithRequest($flow, $request)")
this.flow = flow
var handleProxy: HandleProxy? = null
try {
if (!LOW_LATENCY_ENABLED || flow in NOT_LOW_LATENCY_FLOWS) {
handleProxy = null
} else {
try {
handleProxy = factory.createLowLatencyHandle(flow, callback, request)
Log.d(TAG, "Using low-latency handle")
} catch (e: Exception) {
Log.w(TAG, e)
handleProxy = null
}
}
if (handleProxy == null) {
handleProxy = factory.createHandle(packageName, flow, callback, request)
}
if (handleProxy.init()) {
this.handleProxy = handleProxy
} else {
throw Exception("init failed")
}
} catch (e: Exception) {
Log.w(TAG, "Error during handle init", e)
this.handleInitError = e
}
this.condition.open()
if (handleInitError == null) {
try {
val `object` = handleProxy!!.handle.javaClass.getDeclaredMethod("rb").invoke(handleProxy.handle) as? Parcelable?
if (`object` != null) {
val vmKey = handleProxy.vmKey
val theApk = factory.getTheApkFile(vmKey)
try {
theApk.setReadable(true, false)
return DroidGuardInitReply(ParcelFileDescriptor.open(theApk, ParcelFileDescriptor.MODE_READ_ONLY), `object`)
} catch (e: FileNotFoundException) {
throw Exception("Files for VM $vmKey not found on disk")
}
}
} catch (e: Exception) {
this.handleProxy = null
handleInitError = e
}
}
return DroidGuardInitReply(null, null)
}
override fun snapshot(map: MutableMap<Any?, Any?>): ByteArray {
Log.d(TAG, "snapshot($map)")
condition.block()
handleInitError?.let { return FallbackCreator.create(flow, context, map, it) }
val handleProxy = this.handleProxy ?: return FallbackCreator.create(flow, context, map, IllegalStateException())
return try {
handleProxy.handle::class.java.getDeclaredMethod("ss", Map::class.java).invoke(handleProxy.handle, map) as ByteArray
} catch (e: Exception) {
try {
throw BytesException(handleProxy.extra, e)
} catch (e2: Exception) {
FallbackCreator.create(flow, context, map, e2)
}
}
}
override fun close() {
Log.d(TAG, "close()")
condition.block()
try {
handleProxy?.close()
} catch (e: Exception) {
Log.w(TAG, "Error during handle close", e)
}
handleProxy = null
handleInitError = null
}
companion object {
private const val TAG = "GmsGuardHandleImpl"
private val LOW_LATENCY_ENABLED = false
private val NOT_LOW_LATENCY_FLOWS = setOf("ad_attest", "attest", "checkin", "federatedMachineLearningReduced", "msa-f", "ad-event-attest-token")
}
}

View file

@ -0,0 +1,69 @@
/*
* SPDX-FileCopyrightText: 2021 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import androidx.core.database.getStringOrNull
import org.microg.gms.settings.SettingsContract
import org.microg.gms.settings.SettingsContract.DroidGuard.ENABLED
import org.microg.gms.settings.SettingsContract.DroidGuard.FORCE_LOCAL_DISABLED
import org.microg.gms.settings.SettingsContract.DroidGuard.HARDWARE_ATTESTATION_BLOCKED
import org.microg.gms.settings.SettingsContract.DroidGuard.MODE
import org.microg.gms.settings.SettingsContract.DroidGuard.NETWORK_SERVER_URL
object DroidGuardPreferences {
private fun <T> getSettings(context: Context, projection: String, def: T, f: (Cursor) -> T): T {
return try {
SettingsContract.getSettings(context, SettingsContract.DroidGuard.getContentUri(context), arrayOf(projection), f)
} catch (e: Exception) {
def
}
}
private fun setSettings(context: Context, f: ContentValues.() -> Unit) =
SettingsContract.setSettings(context, SettingsContract.DroidGuard.getContentUri(context), f)
@JvmStatic
fun isForcedLocalDisabled(context: Context): Boolean = getSettings(context, FORCE_LOCAL_DISABLED, false) { it.getInt(0) != 0 }
@JvmStatic
fun isEnabled(context: Context): Boolean = getSettings(context, ENABLED, false) { it.getInt(0) != 0 }
@JvmStatic
fun isAvailable(context: Context): Boolean = isEnabled(context) && (!isForcedLocalDisabled(context) || getMode(context) != Mode.Embedded)
@JvmStatic
fun isLocalAvailable(context: Context): Boolean = isEnabled(context) && !isForcedLocalDisabled(context) && getMode(context) == Mode.Embedded
@JvmStatic
fun setEnabled(context: Context, enabled: Boolean) = setSettings(context) { put(ENABLED, enabled) }
@JvmStatic
fun getMode(context: Context): Mode = getSettings(context, MODE, Mode.Embedded) { c -> Mode.valueOf(c.getString(0)) }
@JvmStatic
fun setMode(context: Context, mode: Mode) = setSettings(context) { put(MODE, mode.toString()) }
@JvmStatic
fun getNetworkServerUrl(context: Context): String? = getSettings(context, NETWORK_SERVER_URL, null) { c -> c.getStringOrNull(0) }
@JvmStatic
fun setNetworkServerUrl(context: Context, url: String?) = setSettings(context) { put(NETWORK_SERVER_URL, url) }
@JvmStatic
fun isHardwareAttestationBlocked(context: Context) = getSettings(context, HARDWARE_ATTESTATION_BLOCKED, false) { it.getInt(0) != 0 }
@JvmStatic
fun setHardwareAttestationBlocked(context: Context, value: Boolean) = setSettings(context) { put(HARDWARE_ATTESTATION_BLOCKED, value) }
enum class Mode {
Embedded,
Network
}
}

View file

@ -0,0 +1,11 @@
/*
* SPDX-FileCopyrightText: 2021, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core
import com.google.android.gms.droidguard.DroidGuardChimeraService
import org.microg.gms.chimera.ServiceLoader
import org.microg.gms.chimera.ServiceProxy
class DroidGuardService : ServiceProxy(ServiceLoader.static<DroidGuardChimeraService>())

View file

@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: 2021 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core
import com.google.android.gms.common.internal.GetServiceRequest
import com.google.android.gms.common.internal.IGmsCallbacks
import com.google.android.gms.droidguard.DroidGuardChimeraService
import org.microg.gms.AbstractGmsServiceBroker
import org.microg.gms.common.GmsService
import org.microg.gms.common.PackageUtils
import java.util.*
class DroidGuardServiceBroker(val service: DroidGuardChimeraService) : AbstractGmsServiceBroker(EnumSet.of(GmsService.DROIDGUARD)) {
override fun getService(callback: IGmsCallbacks?, request: GetServiceRequest?) {
handleServiceRequest(callback, request, null)
}
override fun handleServiceRequest(callback: IGmsCallbacks?, request: GetServiceRequest?, service: GmsService?) {
val packageName = PackageUtils.getAndCheckCallingPackageOrImpersonation(this.service, request!!.packageName)
callback!!.onPostInitComplete(0, DroidGuardServiceImpl(this.service, packageName!!), null)
}
}

View file

@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2021 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core
import android.util.Log
import com.google.android.gms.droidguard.DroidGuardChimeraService
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
import com.google.android.gms.droidguard.internal.IDroidGuardCallbacks
import com.google.android.gms.droidguard.internal.IDroidGuardHandle
import com.google.android.gms.droidguard.internal.IDroidGuardService
class DroidGuardServiceImpl(private val service: DroidGuardChimeraService, private val packageName: String) : IDroidGuardService.Stub() {
override fun guard(callbacks: IDroidGuardCallbacks?, flow: String?, map: MutableMap<Any?, Any?>?) {
Log.d(TAG, "guard()")
guardWithRequest(callbacks, flow, map, null)
}
override fun guardWithRequest(callbacks: IDroidGuardCallbacks?, flow: String?, map: MutableMap<Any?, Any?>?, request: DroidGuardResultsRequest?) {
Log.d(TAG, "guardWithRequest()")
TODO("Not yet implemented")
}
override fun getHandle(): IDroidGuardHandle {
Log.d(TAG, "getHandle()")
return when (DroidGuardPreferences.getMode(service)) {
DroidGuardPreferences.Mode.Embedded -> DroidGuardHandleImpl(service, packageName, service.b, service.b(packageName))
DroidGuardPreferences.Mode.Network -> RemoteHandleImpl(service, packageName)
}
}
override fun getClientTimeoutMillis(): Int {
Log.d(TAG, "getClientTimeoutMillis()")
return 60000
}
companion object {
const val TAG = "GmsGuardServiceImpl"
}
}

View file

@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2021 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core
import android.content.Context
import android.util.Log
object FallbackCreator {
private val FAST_FAIL = setOf("ad_attest", "recaptcha-frame", "federatedMachineLearningReduced", "msa-f", "ad-event-attest-token")
@JvmStatic
fun create(flow: String?, context: Context, map: Map<Any?, Any?>, e: Throwable): ByteArray {
Log.w("DGFallback", "create($flow)")
return if (flow in FAST_FAIL) {
"ERROR : no fallback for $flow".encodeToByteArray()
} else {
try {
create(map, null, flow, context, e)
} catch (e: Throwable) {
Log.w("DGFallback", e)
"ERROR : $e".encodeToByteArray()
}
}
}
@JvmStatic
fun create(map: Map<Any?, Any?>, bytes: ByteArray?, flow: String?, context: Context, e: Throwable): ByteArray {
TODO("Not yet implemented")
}
}

View file

@ -0,0 +1,101 @@
/*
* SPDX-FileCopyrightText: 2025 microG Project Team
* SPDX-License-Identifier: Apache-2.0
* Notice: This is heavily inspired by "Universal SafetyNet Fix", used under the terms of MIT License,
* Copyright (c) 2021 Danny Lin <danny@kdrag0n.dev>
*/
package org.microg.gms.droidguard.core
import android.util.Log
import androidx.annotation.Keep
import java.io.InputStream
import java.io.OutputStream
import java.security.*
import java.security.cert.Certificate
import java.util.*
private const val TAG = "DroidGuard"
class HardwareAttestationBlockingProvider(
realProvider: Provider,
realSpi: KeyStoreSpi
) : Provider(realProvider.name, realProvider.version, realProvider.info) {
init {
HardwareAttestationBlockingKeyStore.realSpi = realSpi
this["KeyStore.$PROVIDER_NAME"] = HardwareAttestationBlockingKeyStore::class.java.name
}
companion object {
private var currentlyEnabled = false
private lateinit var originalProvider: Provider
private const val PROVIDER_NAME = "AndroidKeyStore"
private const val FIELD_KEY_STORE_SPI = "keyStoreSpi"
@JvmStatic
fun ensureEnabled(enabled: Boolean = true) {
if (currentlyEnabled == enabled) return
try {
if (enabled) {
Log.d(TAG, "Hardware attestation blocking enabled")
originalProvider = Security.getProvider(PROVIDER_NAME)
val realKeystore = KeyStore.getInstance(PROVIDER_NAME)
val realSpi = realKeystore.get<KeyStoreSpi>(FIELD_KEY_STORE_SPI)
val newProvider = HardwareAttestationBlockingProvider(originalProvider, realSpi)
Security.removeProvider(PROVIDER_NAME)
Security.insertProviderAt(newProvider, 1)
currentlyEnabled = true
} else {
Log.d(TAG, "Hardware attestation blocking disabled")
Security.removeProvider(PROVIDER_NAME)
Security.insertProviderAt(originalProvider, 1)
currentlyEnabled = false
}
} catch (e: Exception) {
Log.w(TAG, "Failed replacing the security provider", e)
}
}
}
}
class HardwareAttestationBlockingKeyStore(private val realSpi: KeyStoreSpi) : KeyStoreSpi() {
@Keep
constructor() : this(Companion.realSpi ?: throw IllegalStateException())
override fun engineGetCertificateChain(alias: String?): Array<Certificate>? {
for (stackTraceElement in Thread.currentThread().getStackTrace()) {
if (stackTraceElement.className.lowercase().contains("droidguard")) {
Log.d(TAG, "Block DroidGuard from accessing engineGetCertificateChain")
throw UnsupportedOperationException()
}
}
return realSpi.engineGetCertificateChain(alias)
}
override fun engineGetKey(alias: String?, password: CharArray?): Key? = realSpi.engineGetKey(alias, password)
override fun engineGetCertificate(alias: String?): Certificate? = realSpi.engineGetCertificate(alias)
override fun engineGetCreationDate(alias: String?): Date? = realSpi.engineGetCreationDate(alias)
override fun engineSetKeyEntry(alias: String?, key: Key?, password: CharArray?, chain: Array<out Certificate>?) = realSpi.engineSetKeyEntry(alias, key, password, chain)
override fun engineSetKeyEntry(alias: String?, key: ByteArray?, chain: Array<out Certificate>?) = realSpi.engineSetKeyEntry(alias, key, chain)
override fun engineSetCertificateEntry(alias: String?, cert: Certificate?) = realSpi.engineSetCertificateEntry(alias, cert)
override fun engineDeleteEntry(alias: String?) = realSpi.engineDeleteEntry(alias)
override fun engineAliases(): Enumeration<String>? = realSpi.engineAliases()
override fun engineContainsAlias(alias: String?) = realSpi.engineContainsAlias(alias)
override fun engineSize() = realSpi.engineSize()
override fun engineIsKeyEntry(alias: String?) = realSpi.engineIsKeyEntry(alias)
override fun engineIsCertificateEntry(alias: String?) = realSpi.engineIsCertificateEntry(alias)
override fun engineGetCertificateAlias(cert: Certificate?): String? = realSpi.engineGetCertificateAlias(cert)
override fun engineStore(stream: OutputStream?, password: CharArray?) = realSpi.engineStore(stream, password)
override fun engineLoad(stream: InputStream?, password: CharArray?) = realSpi.engineLoad(stream, password)
companion object {
var realSpi: KeyStoreSpi? = null
}
}
private fun <T> Any.get(name: String) = this::class.java.getDeclaredField(name).let { field ->
field.isAccessible = true
@Suppress("unchecked_cast")
field.get(this) as T
}

View file

@ -0,0 +1,177 @@
/*
* SPDX-FileCopyrightText: 2021 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core
import android.content.Context
import com.android.volley.NetworkResponse
import com.android.volley.VolleyError
import com.android.volley.toolbox.RequestFuture
import com.android.volley.toolbox.Volley
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
import okio.ByteString.Companion.decodeHex
import okio.ByteString.Companion.of
import org.microg.gms.droidguard.*
import org.microg.gms.profile.Build
import org.microg.gms.profile.ProfileManager
import org.microg.gms.utils.singleInstanceOf
import java.io.File
import java.util.*
import com.android.volley.Request as VolleyRequest
import com.android.volley.Response as VolleyResponse
class NetworkHandleProxyFactory(private val context: Context) : HandleProxyFactory(context) {
private val dgDb: DgDatabaseHelper = DgDatabaseHelper(context)
private val version = VersionUtil(context)
private val queue = singleInstanceOf { Volley.newRequestQueue(context.applicationContext) }
fun createHandle(packageName: String, flow: String?, callback: GuardCallback, request: DroidGuardResultsRequest?): HandleProxy {
if (!DroidGuardPreferences.isLocalAvailable(context)) throw IllegalAccessException("DroidGuard should not be available locally")
val (vmKey, byteCode, bytes) = readFromDatabase(flow) ?: fetchFromServer(flow, packageName)
return createHandleProxy(flow, vmKey, byteCode, bytes, callback, request)
}
fun createPingHandle(packageName: String, flow: String, callback: GuardCallback, pingData: PingData?): HandleProxy {
if (!DroidGuardPreferences.isLocalAvailable(context)) throw IllegalAccessException("DroidGuard should not be available locally")
val (vmKey, byteCode, bytes) = fetchFromServer(flow, createRequest(flow, packageName, pingData))
return createHandleProxy(flow, vmKey, byteCode, bytes, callback, DroidGuardResultsRequest().also { it.clientVersion = 0 })
}
fun createLowLatencyHandle(flow: String?, callback: GuardCallback, request: DroidGuardResultsRequest?): HandleProxy {
if (!DroidGuardPreferences.isLocalAvailable(context)) throw IllegalAccessException("DroidGuard should not be available locally")
val (vmKey, byteCode, bytes) = readFromDatabase("fast") ?: throw Exception("low latency (fast) flow not available")
return createHandleProxy(flow, vmKey, byteCode, bytes, callback, request)
}
fun SignedResponse.unpack(): Response {
if (SignatureVerifier.verifySignature(data_!!.toByteArray(), signature!!.toByteArray())) {
return Response.ADAPTER.decode(data_!!)
} else {
throw SecurityException("Signature invalid")
}
}
private fun readFromDatabase(flow: String?): Triple<String, ByteArray, ByteArray>? {
ProfileManager.ensureInitialized(context)
val id = "$flow/${version.versionString}/${Build.FINGERPRINT}"
return dgDb.get(id)
}
fun createRequest(flow: String?, packageName: String, pingData: PingData? = null, extra: ByteArray? = null): Request {
ProfileManager.ensureInitialized(context)
return Request(
usage = Usage(flow, packageName),
info = listOf(
KeyValuePair("BOARD", Build.BOARD),
KeyValuePair("BOOTLOADER", Build.BOOTLOADER),
KeyValuePair("BRAND", Build.BRAND),
KeyValuePair("CPU_ABI", Build.CPU_ABI),
KeyValuePair("CPU_ABI2", Build.CPU_ABI2),
KeyValuePair("SUPPORTED_ABIS", Build.SUPPORTED_ABIS.joinToString(",")),
KeyValuePair("DEVICE", Build.DEVICE),
KeyValuePair("DISPLAY", Build.DISPLAY),
KeyValuePair("FINGERPRINT", Build.FINGERPRINT),
KeyValuePair("HARDWARE", Build.HARDWARE),
KeyValuePair("HOST", Build.HOST),
KeyValuePair("ID", Build.ID),
KeyValuePair("MANUFACTURER", Build.MANUFACTURER),
KeyValuePair("MODEL", Build.MODEL),
KeyValuePair("PRODUCT", Build.PRODUCT),
KeyValuePair("RADIO", Build.RADIO),
KeyValuePair("SERIAL", Build.SERIAL),
KeyValuePair("TAGS", Build.TAGS),
KeyValuePair("TIME", Build.TIME.toString()),
KeyValuePair("TYPE", Build.TYPE),
KeyValuePair("USER", Build.USER),
KeyValuePair("VERSION.CODENAME", Build.VERSION.CODENAME),
KeyValuePair("VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL),
KeyValuePair("VERSION.RELEASE", Build.VERSION.RELEASE),
KeyValuePair("VERSION.SDK", Build.VERSION.SDK),
KeyValuePair("VERSION.SDK_INT", Build.VERSION.SDK_INT.toString()),
),
versionName = version.versionString,
versionCode = BuildConfig.VERSION_CODE,
hasAccount = false,
isGoogleCn = false,
enableInlineVm = true,
cached = getCacheDir().list()?.map { it.decodeHex() }.orEmpty(),
arch = System.getProperty("os.arch"),
ping = pingData,
field10 = extra?.let { of(*it) },
)
}
fun fetchFromServer(flow: String?, packageName: String): Triple<String, ByteArray, ByteArray> {
return fetchFromServer(flow, createRequest(flow, packageName))
}
fun fetchFromServer(flow: String?, request: Request): Triple<String, ByteArray, ByteArray> {
ProfileManager.ensureInitialized(context)
val future = RequestFuture.newFuture<SignedResponse>()
queue.add(object : VolleyRequest<SignedResponse>(Method.POST, SERVER_URL, future) {
override fun parseNetworkResponse(response: NetworkResponse): VolleyResponse<SignedResponse> {
return try {
VolleyResponse.success(SignedResponse.ADAPTER.decode(response.data), null)
} catch (e: Exception) {
VolleyResponse.error(VolleyError(e))
}
}
override fun deliverResponse(response: SignedResponse) {
future.onResponse(response)
}
override fun getBody(): ByteArray = request.encode()
override fun getBodyContentType(): String = "application/x-protobuf"
override fun getHeaders(): Map<String, String> {
return mapOf(
"User-Agent" to "DroidGuard/${version.versionCode}"
)
}
})
val signed: SignedResponse = future.get()
val response = signed.unpack()
val vmKey = response.vmChecksum!!.hex()
if (!isValidCache(vmKey)) {
val temp = File(getCacheDir(), "${UUID.randomUUID()}.apk")
temp.parentFile!!.mkdirs()
temp.writeBytes(response.content!!.toByteArray())
getOptDir(vmKey).mkdirs()
temp.renameTo(getTheApkFile(vmKey))
updateCacheTimestamp(vmKey)
if (!isValidCache(vmKey)) {
getCacheDir(vmKey).deleteRecursively()
throw IllegalStateException()
}
}
val id = "$flow/${version.versionString}/${Build.FINGERPRINT}"
val expiry = (response.expiryTimeSecs ?: 0).toLong()
val byteCode = response.byteCode?.toByteArray() ?: ByteArray(0)
val extra = response.extra?.toByteArray() ?: ByteArray(0)
if (response.save != false) {
dgDb.put(id, expiry, vmKey, byteCode, extra)
}
return Triple(vmKey, byteCode, extra)
}
private fun createHandleProxy(
flow: String?,
vmKey: String,
byteCode: ByteArray,
extra: ByteArray,
callback: GuardCallback,
request: DroidGuardResultsRequest?
): HandleProxy {
ProfileManager.ensureInitialized(context)
val clazz = loadClass(vmKey, extra)
return HandleProxy(clazz, context, flow, byteCode, callback, vmKey, extra, request?.bundle)
}
companion object {
const val SERVER_URL = "https://www.googleapis.com/androidantiabuse/v1/x/create?alt=PROTO&key=AIzaSyBofcZsgLSS7BOnBjZPEkk4rYwzOIz-lTI"
}
}

View file

@ -0,0 +1,62 @@
/*
* SPDX-FileCopyrightText: 2025 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core
import android.content.Context
import android.net.Uri
import android.util.Base64
import com.google.android.gms.droidguard.internal.DroidGuardInitReply
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
import com.google.android.gms.droidguard.internal.IDroidGuardHandle
import android.util.Log
import java.net.HttpURLConnection
import java.net.URL
private const val TAG = "RemoteGuardImpl"
class RemoteHandleImpl(private val context: Context, private val packageName: String) : IDroidGuardHandle.Stub() {
private var flow: String? = null
private var request: DroidGuardResultsRequest? = null
private val url: String
get() = DroidGuardPreferences.getNetworkServerUrl(context) ?: throw IllegalStateException("Network URL required")
override fun init(flow: String?) {
Log.d(TAG, "init($flow)")
this.flow = flow
}
override fun snapshot(map: Map<Any?, Any?>?): ByteArray {
Log.d(TAG, "snapshot($map)")
val paramsMap = mutableMapOf("flow" to flow, "source" to packageName)
for (key in request?.bundle?.keySet().orEmpty()) {
request?.bundle?.getString(key)?.let { paramsMap["x-request-$key"] = it }
}
val params = paramsMap.map { Uri.encode(it.key) + "=" + Uri.encode(it.value) }.joinToString("&")
val connection = URL("$url?$params").openConnection() as HttpURLConnection
val payload = map.orEmpty().map { Uri.encode(it.key as String) + "=" + Uri.encode(it.value as String) }.joinToString("&")
Log.d(TAG, "POST ${connection.url}: $payload")
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
connection.requestMethod = "POST"
connection.doInput = true
connection.doOutput = true
connection.outputStream.use { it.write(payload.encodeToByteArray()) }
val bytes = connection.inputStream.use { it.readBytes() }.decodeToString()
return Base64.decode(bytes, Base64.URL_SAFE + Base64.NO_WRAP + Base64.NO_PADDING)
}
override fun close() {
Log.d(TAG, "close()")
this.request = null
this.flow = null
}
override fun initWithRequest(flow: String?, request: DroidGuardResultsRequest?): DroidGuardInitReply? {
Log.d(TAG, "initWithRequest($flow, $request)")
this.flow = flow
this.request = request
return null
}
}

View file

@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 2021 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core
import android.util.Base64
import android.util.Log
import java.security.KeyFactory
import java.security.Signature
import java.security.spec.X509EncodedKeySpec
object SignatureVerifier {
const val TAG = "GmsGuardSigVerify"
const val PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxW77dCKJ8mhEIfXXdeidi7/7LMNM/fzwI+wj1Ed8xIKgTYWCnekRko3JxQb4Cv/gEL5hEA8e9lFs3V67VUL6hCo1FxysXj7Q8n3Kp7hARDkbiZ0mdk8bSanqrPAXTPx6pEL2ZOzfFCHEtJdhz5Ozp2C4XTKF1SBv/YbpsqSUJwdhG7ZPGjyCMRloMww6ITpGdVQ8lChklkCek0WPbz2UrY5RC1qIJKmmcB6KNxxE776Dn6QoYbhN5jPeVBp7lDD3UxjfVzTxKKDAome6fUVBop3dpcLM6rq3+nNT2YArgqTD1qtsVM9vHlcLaAYaPg82vtIN80iDUseMlVHgK+nf6wIDAQAB"
fun verifySignature(data: ByteArray, signature: ByteArray): Boolean {
try {
val keyFactory = KeyFactory.getInstance("RSA") ?: return false
val sig = Signature.getInstance("SHA256withRSA") ?: return false
val keySpec = X509EncodedKeySpec(Base64.decode(PUBLIC_KEY, 0))
sig.initVerify(keyFactory.generatePublic(keySpec))
sig.update(data)
return sig.verify(signature)
} catch (e: Exception) {
Log.w(TAG, e)
return false
}
}
}

View file

@ -0,0 +1,76 @@
/*
* SPDX-FileCopyrightText: 2021 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core
import android.content.Context
import org.microg.gms.profile.Build
import org.microg.gms.profile.ProfileManager
class VersionUtil(private val context: Context) {
val buildType: String
get() {
ProfileManager.ensureInitialized(context)
// Note: Android TV and Watch use different version codes
val versionCode = when (Build.VERSION.SDK_INT) {
31 -> "19"
30 -> "15"
29 -> "12"
28 -> "10"
23, 24, 25, 26, 27 -> "04"
21, 22 -> "02"
else -> "00"
}
val architectureCode = when (Build.CPU_ABI) {
"x86_64" -> "08"
"x86" -> "07"
"arm64-v8a" -> "04"
"arm", "armeabi", "armeabi-v7a" -> "03"
else -> "00"
}
val dpiCode = when (context.resources.displayMetrics.densityDpi) { // TODO: Also something to get from profile
160 -> "02"
240 -> "04"
320 -> "06"
480 -> "08"
else -> "00"
}
val type = "$versionCode$architectureCode$dpiCode"
if (isKnown(type)) return type
val nodpi = "$versionCode${architectureCode}00"
if (isKnown(nodpi)) return nodpi // Fallback to nodpi for increased compat
return type // Use unknown build type
}
val versionString: String
get() = "${BuildConfig.VERSION_NAME} ($buildType-{{cl}})"
val versionCode: Int
get() = BuildConfig.VERSION_CODE + (getVersionOffset(buildType) ?: 0)
fun isKnown(type: String): Boolean = getVersionOffset(type) != null
fun getVersionOffset(type: String): Int? {
val v1 = type.substring(0, 2)
val v2 = type.substring(2, 4)
val v3 = type.substring(4, 6)
val i1 = BUILD_MAP.indexOfFirst { it.first == v1 }.takeIf { it >= 0 } ?: return null
val i2 = BUILD_MAP[i1].second.indexOfFirst { it.first == v2 }.takeIf { it >= 0 } ?: return null
val i3 = BUILD_MAP[i1].second[i2].second.indexOf(v3).takeIf { it > 0 } ?: return null
val o1 = BUILD_MAP.subList(0, i1).map { it.second.map { it.second.size }.sum() }.sum()
val o2 = BUILD_MAP[i1].second.subList(0, i2).map { it.second.size }.sum()
return o1 + o2 + i3
}
companion object {
val BUILD_MAP = listOf(
"00" to listOf("03" to listOf("00", "02", "04", "06", "08"), "07" to listOf("00")),
"02" to listOf("03" to listOf("00", "04", "06", "08"), "04" to listOf("00", "06", "08"), "07" to listOf("00"), "08" to listOf("00")),
"04" to listOf("03" to listOf("00", "04", "06", "08"), "04" to listOf("00", "06", "08"), "07" to listOf("00"), "08" to listOf("00")),
"10" to listOf("03" to listOf("00", "04", "06", "08"), "04" to listOf("00", "06", "08"), "07" to listOf("00"), "08" to listOf("00")),
"12" to listOf("03" to listOf("00", "04", "06", "08"), "04" to listOf("00", "06", "08"), "07" to listOf("00"), "08" to listOf("00")),
"15" to listOf("03" to listOf("00", "04", "06", "08"), "04" to listOf("00", "06", "08"), "07" to listOf("00"), "08" to listOf("00")),
"19" to listOf("03" to listOf("00", "08"), "04" to listOf("00", "08"), "07" to listOf("00"), "08" to listOf("00")),
)
}
}

View file

@ -0,0 +1,70 @@
/*
* SPDX-FileCopyrightText: 2021 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core.ui
import android.content.Context
import android.text.TextWatcher
import android.util.AttributeSet
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.core.widget.addTextChangedListener
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import org.microg.gms.droidguard.core.R
class ContainedEditTextPreference : Preference {
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context) : super(context)
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
val editText = holder.itemView.findViewById<EditText>(android.R.id.edit)
(editText as? TextWatcher)?.let { editText.removeTextChangedListener(it) }
editText.addTextChangedListener { textChangedListener(it?.toString() ?: "") }
editText.tag = this
editText.hint = hint
editText.text.replace(0, editText.text.length, text)
editText.isEnabled = editable
if (requestFocus) {
editText.requestFocus()
(editText.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT)
requestFocus = false
}
}
private var requestFocus: Boolean = false
fun editRequestFocus() {
requestFocus = true
notifyChanged()
}
var textChangedListener: (String) -> Unit = {}
var editable: Boolean = true
set(value) {
field = value
notifyChanged()
}
var text: String = ""
set(value) {
field = value
notifyChanged()
}
var hint: String = ""
set(value) {
field = value
notifyChanged()
}
init {
layoutResource = R.layout.preference_material_with_widget_below
widgetLayoutResource = R.layout.preference_edit_widget
}
}

View file

@ -0,0 +1,66 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core.ui
import android.annotation.SuppressLint
import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.TwoStatePreference
import org.microg.gms.droidguard.core.DroidGuardPreferences
import org.microg.gms.droidguard.core.DroidGuardPreferences.Mode.Embedded
import org.microg.gms.droidguard.core.DroidGuardPreferences.Mode.Network
import org.microg.gms.droidguard.core.R
import org.microg.gms.base.core.R.drawable.ic_radio_checked
import org.microg.gms.base.core.R.drawable.ic_radio_unchecked
class DroidGuardPreferencesFragment : PreferenceFragmentCompat() {
private lateinit var modeEmbedded: Preference
private lateinit var modeNetwork: ContainedEditTextPreference
private lateinit var blockHardwareAttestation: TwoStatePreference
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences_droidguard)
}
@SuppressLint("RestrictedApi")
override fun onBindPreferences() {
modeEmbedded = preferenceScreen.findPreference("pref_droidguard_mode_embedded") ?: modeEmbedded
modeNetwork = preferenceScreen.findPreference("pref_droidguard_mode_network") ?: modeNetwork
blockHardwareAttestation = preferenceScreen.findPreference("pref_droidguard_block_hw_attestation") ?: blockHardwareAttestation
modeEmbedded.setOnPreferenceClickListener {
DroidGuardPreferences.setMode(it.context, Embedded)
updateConfiguration()
true
}
modeNetwork.setOnPreferenceClickListener {
DroidGuardPreferences.setMode(it.context, Network)
modeNetwork.editRequestFocus()
updateConfiguration()
true
}
modeNetwork.textChangedListener = {
DroidGuardPreferences.setNetworkServerUrl(requireContext(), it)
}
blockHardwareAttestation.setOnPreferenceChangeListener { _, newValue ->
DroidGuardPreferences.setHardwareAttestationBlocked(requireContext(), newValue as Boolean)
true
}
modeEmbedded.isEnabled = !DroidGuardPreferences.isForcedLocalDisabled(requireContext())
blockHardwareAttestation.isChecked = DroidGuardPreferences.isHardwareAttestationBlocked(requireContext())
updateConfiguration()
}
fun updateConfiguration() {
val mode = DroidGuardPreferences.getMode(requireContext())
modeEmbedded.setIcon(if (mode == Embedded) ic_radio_checked else ic_radio_unchecked)
modeNetwork.setIcon(if (mode == Network) ic_radio_checked else ic_radio_unchecked)
modeNetwork.text = DroidGuardPreferences.getNetworkServerUrl(requireContext()) ?: ""
modeNetwork.editable = mode == Network
modeNetwork.hint = "https://example.com/droidguard/"
blockHardwareAttestation.isEnabled = mode == Embedded
}
}

View file

@ -0,0 +1,73 @@
/*
* SPDX-FileCopyrightText: 2016, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
option java_package = "org.microg.gms.droidguard";
message Usage {
optional string flow = 1;
optional string packageName = 2;
}
message KeyValuePair {
optional string key = 1;
optional string val = 2;
}
message StackTraceElement {
optional string className = 1;
optional string methodName = 2;
optional string fileName = 3;
optional int32 lineNumber = 4;
optional bool isNativeMethod = 5;
}
message ExceptionInfo {
optional string name = 1;
optional string message = 2;
repeated StackTraceElement stackTrace = 3;
}
message ExceptionList {
repeated ExceptionInfo exceptions = 1;
}
message PingData {
optional string field1 = 1;
optional int64 field2 = 2;
}
message Request {
optional Usage usage = 1;
repeated KeyValuePair info = 2;
optional string versionName = 3;
optional bool hasAccount = 6;
optional bool isGoogleCn = 7;
optional bool enableInlineVm = 8;
repeated bytes cached = 9;
optional bytes field10 = 10;
optional int32 field11 = 11;
optional ExceptionList exceptions = 12;
optional int32 versionCode = 13;
optional string arch = 14;
optional int32 field15 = 15;
optional PingData ping = 16;
}
message SignedResponse {
optional bytes data = 1;
optional bytes signature = 2;
}
message Response {
optional bytes byteCode = 1;
optional string vmUrl = 2;
optional bytes vmChecksum = 3;
optional int32 expiryTimeSecs = 4;
optional bytes content = 5;
optional bool save = 6;
optional int32 minWait = 7;
optional int32 maxWait = 8;
optional bytes extra = 9;
}

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ SPDX-FileCopyrightText: 2021 microG Project Team
~ SPDX-License-Identifier: Apache-2.0
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@android:id/edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusedByDefault="false"
android:inputType="textUri"
android:lines="1"
android:maxLines="1" />
</LinearLayout>

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 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
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:gravity="center_vertical"
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/selectableItemBackground"
android:clipToPadding="false"
android:baselineAligned="false">
<include layout="@layout/image_frame"/>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"/>
<TextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignLeft="@android:id/title"
android:layout_alignStart="@android:id/title"
android:layout_gravity="start"
android:textAlignment="viewStart"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10"
style="@style/PreferenceSummaryTextStyle"/>
<!-- Preference should place its actual preference widget here. -->
<LinearLayout
android:id="@android:id/widget_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@android:id/summary"
android:layout_alignLeft="@android:id/title"
android:layout_alignStart="@android:id/title"
android:gravity="end|center_vertical"
android:orientation="vertical"/>
</RelativeLayout>
</LinearLayout>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">وضع تشغيل درويدغارد</string>
<string name="pref_droidguard_mode_embedded_title">مدمج</string>
<string name="pref_droidguard_mode_embedded_summary">استخدم نظام تنفيد درويدغارد المحلي</string>
<string name="pref_droidguard_mode_network_title">عن بعد</string>
<string name="pref_droidguard_mode_network_summary">اتصل بنظام تنفيذ درويدغارد عبر الشبكة</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pref_droidguard_mode_network_summary">Şəbəkə vasitəsilə DroidGuard iş vaxtına qoşul</string>
<string name="prefcat_droidguard_mode">DroidGuard iş rejimi</string>
<string name="pref_droidguard_mode_embedded_title">Daxili</string>
<string name="pref_droidguard_mode_embedded_summary">Yerli DroidGuard iş vaxtın istifadə et</string>
<string name="pref_droidguard_mode_network_title">Uzaqdan</string>
</resources>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ SPDX-FileCopyrightText: 2021 microG Project Team
~ SPDX-License-Identifier: Apache-2.0
-->
<resources>
<string name="prefcat_droidguard_mode">Рэжым работы DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">Лакальны</string>
<string name="pref_droidguard_mode_embedded_summary">Выкарыстоўваць лакальнае асяроддзе выканання DroidGuard</string>
<string name="pref_droidguard_mode_network_title">Выдалены</string>
<string name="pref_droidguard_mode_network_summary">Падключыцца да асяроддзя выканання DroidGuard па сетцы</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pref_droidguard_mode_embedded_summary">Použít místní běhové prostředí DroidGuard</string>
<string name="pref_droidguard_mode_network_summary">Připojit se k běhovému prostředí DroidGuard pomocí sítě</string>
<string name="prefcat_droidguard_mode">Režim operace DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">Vložený</string>
<string name="pref_droidguard_mode_network_title">Vzdálený</string>
<string name="pref_droidguard_block_hw_attestation_title">Blokovat hardwarovou atestaci</string>
<string name="pref_droidguard_block_hw_attestation_summary">Při používání vložené služby DroidGuard zakázat přístup k hardwarové atestaci. Může pomoci projít kontrolami integrity na některých zařízeních.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">DroidGuard-Betriebsmodus</string>
<string name="pref_droidguard_mode_embedded_title">Eingebettet</string>
<string name="pref_droidguard_mode_embedded_summary">Lokale DroidGuard-Laufzeit verwenden</string>
<string name="pref_droidguard_mode_network_summary">Über das Netzwerk mit der DroidGuard-Laufzeit verbinden</string>
<string name="pref_droidguard_mode_network_title">Externe</string>
<string name="pref_droidguard_block_hw_attestation_title">Hardware-Attestierung blockieren</string>
<string name="pref_droidguard_block_hw_attestation_summary">Bei Verwendung des integrierten DroidGuard den Zugriff auf die Hardware-Attestierung deaktivieren. Dies kann dazu beitragen, dass Integritätsprüfungen auf einigen Geräten erfolgreich durchgeführt werden können.</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">Modo de funcionamiento de DroidGuard</string>
<string name="pref_droidguard_mode_network_summary">Conectar a DroidGuard el tiempo de ejecución a través de la red</string>
<string name="pref_droidguard_mode_embedded_title">Incorporado</string>
<string name="pref_droidguard_mode_embedded_summary">Utilice el tiempo de ejecución local de DroidGuard</string>
<string name="pref_droidguard_mode_network_title">A distancia</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">حالت عملکرد DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">جاسازی شده</string>
<string name="pref_droidguard_mode_embedded_summary">استفاده از زمان اجرا محلی DroidGuard</string>
<string name="pref_droidguard_mode_network_title">راه دور</string>
<string name="pref_droidguard_mode_network_summary">اتصال به زمان اجرا DroidGuard با شبکه</string>
<string name="pref_droidguard_block_hw_attestation_title">تأییدیه بلوک سخت افزار</string>
<string name="pref_droidguard_block_hw_attestation_summary">زمانی که از DroidGuard جاسازی شده استفاده میکنید، دسترسی به تأییدیه سخت افزار را غیرفعال کنید. این کار ممکن است به قبولی در بررسی‌های یکپارچگی، در برخی دستگاه ها کمک کند.</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pref_droidguard_mode_embedded_summary">Gamitin ang lokal na DroidGuard runtime</string>
<string name="pref_droidguard_mode_network_summary">Kumonekta sa DroidGuard runtime sa pamamagitan ng network</string>
<string name="pref_droidguard_mode_network_title">Remote</string>
<string name="prefcat_droidguard_mode">Mode ng operasyon ng DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">Naka-embed</string>
<string name="pref_droidguard_block_hw_attestation_title">Harangan ang pagpapatunay gamit ng hardware</string>
<string name="pref_droidguard_block_hw_attestation_summary">Kapag gumagamit ng embedded na DroidGuard, i-disable ang access sa hardware na pagpapatunay. Maaaring makatulong sa pagpasa ng mga pagsusuri sa integridad sa ilang device.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">Mode d\'opération DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">Embarqué</string>
<string name="pref_droidguard_mode_network_title">A distance</string>
<string name="pref_droidguard_mode_embedded_summary">Exécuter localement DroidGuard</string>
<string name="pref_droidguard_mode_network_summary">Exécuter DroidGuard via une connexion réseau</string>
<string name="pref_droidguard_block_hw_attestation_title">Bloquer l\'attestation matérielle</string>
<string name="pref_droidguard_block_hw_attestation_summary">Désactive laccès à lattestation matérielle quand DroidGuard embarqué est utilisé. Peut aider à passer les vérifications d\'intégrité sur certains appareils.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pref_droidguard_mode_network_summary">Ceangail le ham rite DroidGuard tríd an líonra</string>
<string name="pref_droidguard_mode_network_title">Cianda</string>
<string name="prefcat_droidguard_mode">Modh oibríochta DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">Leabaithe</string>
<string name="pref_droidguard_mode_embedded_summary">Úsáid am rite áitiúil DroidGuard</string>
<string name="pref_droidguard_block_hw_attestation_title">Deimhniú crua-earraí bloc</string>
<string name="pref_droidguard_block_hw_attestation_summary">Agus DroidGuard leabaithe in úsáid agat, díchumasaigh rochtain ar dheimhniú crua-earraí. Dfhéadfadh sé seo cabhrú le seiceálacha sláine a rith ar roinnt gléasanna.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">Mode operasi DroidGuard</string>
<string name="pref_droidguard_mode_embedded_summary">Gunakan runtime DroidGuard lokal</string>
<string name="pref_droidguard_mode_embedded_title">Tertanam</string>
<string name="pref_droidguard_mode_network_title">Jarak jauh</string>
<string name="pref_droidguard_mode_network_summary">Hubungkan ke runtime DroidGuard melalui jaringan</string>
<string name="pref_droidguard_block_hw_attestation_title">Blokir pengesahan perangkat keras</string>
<string name="pref_droidguard_block_hw_attestation_summary">Saat menggunakan DroidGuard yang terintegrasi, nonaktifkan akses ke pengesahan perangkat keras. Hal ini mungkin membantu melewati pemeriksaan integritas pada beberapa perangkat.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">Virknihamur DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">Ívafið</string>
<string name="pref_droidguard_mode_network_title">Fjartengt</string>
<string name="pref_droidguard_mode_embedded_summary">Nota staðbundna DroidGuard-keyrsluskrá</string>
<string name="pref_droidguard_mode_network_summary">Tengjast DroidGuard-keyrsluskrá í gegnum netkerfi</string>
<string name="pref_droidguard_block_hw_attestation_title">Loka á sannvottun vélbúnaðar</string>
<string name="pref_droidguard_block_hw_attestation_summary">Þegar verið er að nota ívafið DroidGuard (embedded), skal gera sannvottun vélbúnaðar (hardware attestation) óvirka. Þetta gæti hjálpað til við að standast áreiðanleikaprófanir á sumum tækjum.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">Modalità operativa di DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">Integrata</string>
<string name="pref_droidguard_mode_embedded_summary">Utilizza il runtime locale di DroidGuard</string>
<string name="pref_droidguard_mode_network_title">Remota</string>
<string name="pref_droidguard_mode_network_summary">Effettua la connessione al runtime di DroidGuard attraverso la rete</string>
<string name="pref_droidguard_block_hw_attestation_title">Blocca attestazione hardware</string>
<string name="pref_droidguard_block_hw_attestation_summary">Quando si utilizza DroidGuard integrato, disabilita l\'accesso all\'attestazione hardware. Potrebbe aiutare a superare i controlli di integrità su alcuni dispositivi.</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">DroidGuard操作モード</string>
<string name="pref_droidguard_mode_embedded_title">組み込み型</string>
<string name="pref_droidguard_mode_embedded_summary">ローカルの DroidGuard ランタイムを使用する</string>
<string name="pref_droidguard_mode_network_title">リモート</string>
<string name="pref_droidguard_mode_network_summary">ネットワーク経由でDroidGuardランタイムに接続する</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">DroidGuard 작동 모드</string>
<string name="pref_droidguard_mode_embedded_title">임베디드</string>
<string name="pref_droidguard_mode_embedded_summary">내장된 DroidGuard 런타임 사용</string>
<string name="pref_droidguard_mode_network_title">원격</string>
<string name="pref_droidguard_mode_network_summary">네트워크로 DroidGuard 런타임에 연결</string>
<string name="pref_droidguard_block_hw_attestation_title">하드웨어 증명 차단</string>
<string name="pref_droidguard_block_hw_attestation_summary">임베디드 DroidGuard를 사용할 때, 하드웨어 인증에 대한 접근을 비활성화합니다. 일부 장치에서 무결성 검사를 통과하는 데 도움이 될 수 있습니다.</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">ഡ്രോയിഡ് ഗാർഡ് പ്രവർത്തന രീതി</string>
<string name="pref_droidguard_mode_embedded_title">എംബഡഡ്</string>
<string name="pref_droidguard_mode_network_title">റിമോട്ട്</string>
<string name="pref_droidguard_mode_embedded_summary">ലോക്കൽ ഡ്രോയിഡ് ഗാർഡ് റൺടൈം ഉപയോഗിക്കുക</string>
<string name="pref_droidguard_mode_network_summary">നെറ്റ്‌വർക്ക് വഴി ഡ്രോയിഡ് ഗാർഡ് റൺടൈമിലേക്ക് കണക്റ്റുചെയ്യുക</string>
<string name="pref_droidguard_block_hw_attestation_title">ഹാർഡ്‌വെയർ അറ്റസ്റ്റേഷൻ തടയുക</string>
<string name="pref_droidguard_block_hw_attestation_summary">എംബഡഡ് ഡ്രോയിഡ് ഗാർഡ് ഉപയോഗിക്കുമ്പോൾ, ഹാർഡ്‌വെയർ അറ്റസ്റ്റേഷനിലേക്കുള്ള ആക്‌സസ് പ്രവർത്തനരഹിതമാക്കുക. ചില ഉപകരണങ്ങളിൽ സമഗ്രത പരിശോധനകൾ പാസാക്കാൻ സഹായിച്ചേക്കാം.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">DroidGuard-driftsmodus</string>
<string name="pref_droidguard_mode_embedded_title">Innebygd</string>
<string name="pref_droidguard_mode_embedded_summary">Bruk lokal DroidGuard-runtime</string>
<string name="pref_droidguard_mode_network_title">Ekstern</string>
<string name="pref_droidguard_mode_network_summary">Koble til DroidGuard-runtime over nettverket</string>
<string name="pref_droidguard_block_hw_attestation_title">Klokker maskinvareattestering</string>
<string name="pref_droidguard_block_hw_attestation_summary">Blokker tilgang til maskinvareattestering når innebygd Droidguard er i bruk. Kan hjelpe med å få godkjent integritetssjekker på enkelte enheter.</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pref_droidguard_mode_network_summary">Połącz z usługą DroidGuard przez sieć</string>
<string name="prefcat_droidguard_mode">Tryb działania DroidGuard</string>
<string name="pref_droidguard_mode_embedded_summary">Korzystaj z lokalnej usługi DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">Wbudowany</string>
<string name="pref_droidguard_mode_network_title">Zdalny</string>
<string name="pref_droidguard_block_hw_attestation_title">Blokowanie zaświadczeń sprzętowych</string>
<string name="pref_droidguard_block_hw_attestation_summary">Podczas korzystania z wbudowanego DroidGuard należy wyłączyć dostęp do poświadczeń sprzętowych. Może to pomóc w przejściu kontroli integralności na niektórych urządzeniach.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">Modo de operação do DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">Embutido</string>
<string name="pref_droidguard_mode_embedded_summary">Usar runtime local do DroidGuard</string>
<string name="pref_droidguard_mode_network_title">Remoto</string>
<string name="pref_droidguard_mode_network_summary">Conectar ao runtime do DroidGuard pela rede</string>
<string name="pref_droidguard_block_hw_attestation_title">Bloquear atestação por hardware</string>
<string name="pref_droidguard_block_hw_attestation_summary">Ao usar o DroidGuard embutido, desativar o acesso à atestação por hardware. Pode ajudar a passar os testes de integridade em alguns dispositivos.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">Modo de operação do DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">Embutido</string>
<string name="pref_droidguard_mode_embedded_summary">Usar runtime local do DroidGuard</string>
<string name="pref_droidguard_mode_network_title">Remoto</string>
<string name="pref_droidguard_mode_network_summary">Conectar ao runtime do DroidGuard pela rede</string>
<string name="pref_droidguard_block_hw_attestation_title">Bloquear atestação por hardware</string>
<string name="pref_droidguard_block_hw_attestation_summary">Ao usar o DroidGuard embutido, desative o acesso à atestação por hardware. Pode ajudar a passar os testes de integridade em alguns dispositivos.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pref_droidguard_mode_network_summary">Conectează-te la runtime DroidGuard prin rețea</string>
<string name="prefcat_droidguard_mode">Modul de operare DroidGuard</string>
<string name="pref_droidguard_mode_embedded_summary">Utilizează timpul de rulare local DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">Încorporat</string>
<string name="pref_droidguard_mode_network_title">La distanță</string>
<string name="pref_droidguard_block_hw_attestation_title">Blochează atestarea hardware</string>
<string name="pref_droidguard_block_hw_attestation_summary">Când utilizezi DroidGuard încorporat, dezactivează accesul la atestarea hardware. Ar putea ajuta la trecerea verificărilor de integritate pe unele dispozitive.</string>
</resources>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ SPDX-FileCopyrightText: 2021 microG Project Team
~ SPDX-License-Identifier: Apache-2.0
--><resources>
<string name="prefcat_droidguard_mode">Режим работы DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">Локальный</string>
<string name="pref_droidguard_mode_embedded_summary">Использовать локальную среду выполнения DroidGuard</string>
<string name="pref_droidguard_mode_network_title">Удалённый</string>
<string name="pref_droidguard_mode_network_summary">Подключиться к среде выполнения DroidGuard по сети</string>
<string name="pref_droidguard_block_hw_attestation_summary">При использовании встроенного DroidGuard отключите доступ к аппаратной аттестации. Это может помочь пройти проверку целостности на некоторых устройствах.</string>
<string name="pref_droidguard_block_hw_attestation_title">Блокировать аппаратную аттестацию</string>
</resources>

View file

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pref_droidguard_mode_network_summary">Повежите се са DroidGuard временом извршавања преко мреже</string>
<string name="prefcat_droidguard_mode">DroidGuard режим рада</string>
<string name="pref_droidguard_mode_embedded_summary">Користите локално DroidGuard време извршавања</string>
<string name="pref_droidguard_mode_embedded_title">Уграђени</string>
<string name="pref_droidguard_mode_network_title">Удаљени</string>
<string name="pref_droidguard_block_hw_attestation_title">Блокирај атестацију хардвера</string>
<string name="pref_droidguard_block_hw_attestation_summary">Када користите уграђени DroidGuard, онемогућите приступ атестацији хардвера. Може помоћи у проласку провера интегритета на неким уређајима.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pref_droidguard_mode_network_summary">Anslut till DroidGuard runtime via nätverk</string>
<string name="prefcat_droidguard_mode">DroidGuard driftläge</string>
<string name="pref_droidguard_mode_embedded_summary">Använd lokal DroidGuard-körning</string>
<string name="pref_droidguard_mode_embedded_title">Inbäddad</string>
<string name="pref_droidguard_mode_network_title">Fjärr</string>
<string name="pref_droidguard_block_hw_attestation_title">Blockera hårdvaruintyg</string>
<string name="pref_droidguard_block_hw_attestation_summary">Inaktivera åtkomst till hårdvaruintyg, när du använder inbäddad DroidGuard. Kan hjälpa för att passera integritetskontroller på vissa enheter.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">டிராய்ட்காப்பு செயல்பாட்டு முறை</string>
<string name="pref_droidguard_mode_embedded_title">உட்பொதிக்கப்பட்டது</string>
<string name="pref_droidguard_mode_embedded_summary">உள்ளக டிராய்ட்காப்பு இயக்க நேரத்தைப் பயன்படுத்து</string>
<string name="pref_droidguard_mode_network_title">தொலைநிலை</string>
<string name="pref_droidguard_mode_network_summary">பிணையம் வழியாக டிராய்ட்காப்பு இயக்க நேரத்துடன் இணை</string>
<string name="pref_droidguard_block_hw_attestation_title">வன்பொருள் சான்றளிப்பைத் தடு</string>
<string name="pref_droidguard_block_hw_attestation_summary">உட்பொதிக்கப்பட்ட DroidGuard ஐப் பயன்படுத்தும்போது, வன்பொருள் சான்றளிப்புக்கான அணுகலை முடக்கவும். சில சாதனங்களில் ஒருமைப்பாடு சோதனைகளை அனுப்ப உதவக்கூடும்.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pref_droidguard_mode_embedded_title">ฝังตัว</string>
<string name="pref_droidguard_mode_network_title">ระยะไกล</string>
<string name="pref_droidguard_mode_network_summary">เชื่อมต่อกับรันไทม์ DroidGuard ผ่านเครือข่าย</string>
<string name="prefcat_droidguard_mode">โหมดการทำงานของ DroidGuard</string>
<string name="pref_droidguard_mode_embedded_summary">ใช้รันไทม์ DroidGuard ในเครื่อง</string>
<string name="pref_droidguard_block_hw_attestation_title">ปิดกั้นการตรวจสอบฮาร์ดแวร์</string>
<string name="pref_droidguard_block_hw_attestation_summary">เมื่อใช้ DroidGuard แบบฝัง (embedded) ให้ ปิดการเข้าถึงการยืนยันฮาร์ดแวร์ (hardware attestation) ซึ่งอาจช่วยให้ ผ่านการตรวจสอบความสมบูรณ์ (integrity checks) บนอุปกรณ์บางเครื่องได้</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">DroidGuard çalışma modu</string>
<string name="pref_droidguard_mode_embedded_title">Gömülü</string>
<string name="pref_droidguard_mode_embedded_summary">Yerel DroidGuard çalıştırıcısını kullan</string>
<string name="pref_droidguard_mode_network_title">Uzak</string>
<string name="pref_droidguard_mode_network_summary">Ağ üzerinden DroidGuard çalıştırıcısına bağlan</string>
<string name="pref_droidguard_block_hw_attestation_title">Donanım dayalı sağlamayı engelle</string>
<string name="pref_droidguard_block_hw_attestation_summary">Gömülü DroidGuard\'ı kullanırken donanıma dayalı doğrulamaya erişimi devre dışı bırakır. Bazı cihazlarda bütünlük doğrulama testinden başarıyla geçilmesini sağlayabilir.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pref_droidguard_mode_network_summary">تور ئارقىلىق DroidGuard ئىجرا ۋاقتىغا باغلانغاندا</string>
<string name="prefcat_droidguard_mode">DroidGuard مەشغۇلات ھالىتى</string>
<string name="pref_droidguard_mode_embedded_title">سىڭدۈرۈلگەن</string>
<string name="pref_droidguard_mode_embedded_summary">يەرلىك DroidGuard ئىجرا ۋاقتىنى ئىشلىتىدۇ</string>
<string name="pref_droidguard_mode_network_title">يىراق</string>
<string name="pref_droidguard_block_hw_attestation_title">قاتتىق دېتال دەلىللەشنى توسىدۇ</string>
<string name="pref_droidguard_block_hw_attestation_summary">سىڭدۈرمە DroidGuard ئىشلەتكەندە، قاتتىق دېتال دەلىللەشنى زىيارەت قىلىشنى چەكلەيدۇ. بۇ بەزى ئۈسكۈنىلەردە مۇكەممەللىكنى تەكشۈرۈشتىن ئۆتۈشكە ياردىمى بولۇشى مۇمكىن.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pref_droidguard_mode_network_summary">Під\'єднатися до середовища виконання DroidGuard через мережу</string>
<string name="prefcat_droidguard_mode">Режим роботи DroidGuard</string>
<string name="pref_droidguard_mode_embedded_summary">Використовувати локальне середовище виконання DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">Вбудований</string>
<string name="pref_droidguard_mode_network_title">Віддалений</string>
<string name="pref_droidguard_block_hw_attestation_title">Блокувати підтвердження обладнання</string>
<string name="pref_droidguard_block_hw_attestation_summary">При використанні вбудованого DroidGuard вимкніть доступ до підтвердження апаратного забезпечення. Це може допомогти пройти перевірку цілісності на деяких пристроях.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">Chế độ hoạt động của DroidGuard</string>
<string name="pref_droidguard_mode_embedded_title">Nhúng</string>
<string name="pref_droidguard_mode_embedded_summary">Sử dụng runtime DroidGuard cục bộ</string>
<string name="pref_droidguard_mode_network_title">Từ xa</string>
<string name="pref_droidguard_mode_network_summary">Kết nối đến runtime DroidGuard qua mạng</string>
<string name="pref_droidguard_block_hw_attestation_title">Chặn xác thực phần cứng</string>
<string name="pref_droidguard_block_hw_attestation_summary">Khi dùng DroidGuard nhúng, vô hiệu hóa truy cập xác thực phần cứng. Có thể giúp vượt qua kiểm tra tính toàn vẹn trên một số thiết bị.</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prefcat_droidguard_mode">DroidGuard 运作模式</string>
<string name="pref_droidguard_mode_network_summary">通过网络连接到 DroidGuard 运行时</string>
<string name="pref_droidguard_mode_embedded_title">内置</string>
<string name="pref_droidguard_mode_network_title">远程</string>
<string name="pref_droidguard_mode_embedded_summary">使用本地 DroidGuard 运行时</string>
<string name="pref_droidguard_block_hw_attestation_title">阻止硬件认证</string>
<string name="pref_droidguard_block_hw_attestation_summary">在使用嵌入式DroidGuard时禁用对硬件认证的访问。这可能有助于在某些设备上通过完整性检查。</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pref_droidguard_mode_network_title">遠端</string>
<string name="prefcat_droidguard_mode">DroidGuard 運作模式</string>
<string name="pref_droidguard_mode_embedded_title">內建</string>
<string name="pref_droidguard_mode_embedded_summary">使用本地 DroidGuard runtime</string>
<string name="pref_droidguard_mode_network_summary">透過網路連接到 DroidGuard runtime</string>
</resources>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ SPDX-FileCopyrightText: 2021 microG Project Team
~ SPDX-License-Identifier: Apache-2.0
-->
<resources>
<string name="prefcat_droidguard_mode">DroidGuard operation mode</string>
<string name="pref_droidguard_mode_embedded_title">Embedded</string>
<string name="pref_droidguard_mode_embedded_summary">Use local DroidGuard runtime</string>
<string name="pref_droidguard_mode_network_title">Remote</string>
<string name="pref_droidguard_mode_network_summary">Connect to DroidGuard runtime via network</string>
<string name="pref_droidguard_block_hw_attestation_title">Block hardware attestation</string>
<string name="pref_droidguard_block_hw_attestation_summary">When using embedded DroidGuard, disable access to hardware attestation. Might help passing integrity checks on some devices.</string>
</resources>

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
android:title="@string/prefcat_droidguard_mode"
app:iconSpaceReserved="false">
<Preference
android:icon="@drawable/ic_radio_unchecked"
android:key="pref_droidguard_mode_embedded"
android:persistent="false"
android:summary="@string/pref_droidguard_mode_embedded_summary"
android:title="@string/pref_droidguard_mode_embedded_title" />
<org.microg.gms.droidguard.core.ui.ContainedEditTextPreference
android:icon="@drawable/ic_radio_unchecked"
android:key="pref_droidguard_mode_network"
android:persistent="false"
android:summary="@string/pref_droidguard_mode_network_summary"
android:title="@string/pref_droidguard_mode_network_title" />
</PreferenceCategory>
<SwitchPreferenceCompat
android:defaultValue="true"
android:persistent="false"
android:key="pref_droidguard_block_hw_attestation"
android:summary="@string/pref_droidguard_block_hw_attestation_summary"
android:title="@string/pref_droidguard_block_hw_attestation_title"
app:iconSpaceReserved="false" />
</PreferenceScreen>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ SPDX-FileCopyrightText: 2020, microG Project Team
~ SPDX-License-Identifier: Apache-2.0
-->
<manifest />

View file

@ -0,0 +1,3 @@
package com.google.android.gms.droidguard.internal;
parcelable DroidGuardInitReply;

View file

@ -0,0 +1,3 @@
package com.google.android.gms.droidguard.internal;
parcelable DroidGuardResultsRequest;

View file

@ -0,0 +1,5 @@
package com.google.android.gms.droidguard.internal;
interface IDroidGuardCallbacks {
void onResult(in byte[] res);
}

View file

@ -0,0 +1,11 @@
package com.google.android.gms.droidguard.internal;
import com.google.android.gms.droidguard.internal.DroidGuardInitReply;
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest;
interface IDroidGuardHandle {
oneway void init(String flow) = 0;
byte[] snapshot(in Map map) = 1;
oneway void close() = 2;
DroidGuardInitReply initWithRequest(String flow, in DroidGuardResultsRequest request) = 4;
}

View file

@ -0,0 +1,14 @@
package com.google.android.gms.droidguard.internal;
import com.google.android.gms.droidguard.internal.IDroidGuardCallbacks;
import com.google.android.gms.droidguard.internal.IDroidGuardHandle;
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest;
interface IDroidGuardService {
void guard(IDroidGuardCallbacks callbacks, String flow, in Map map) = 0;
void guardWithRequest(IDroidGuardCallbacks callbacks, String flow, in Map map, in DroidGuardResultsRequest request) = 3;
IDroidGuardHandle getHandle() = 1;
int getClientTimeoutMillis() = 2;
}

View file

@ -0,0 +1,19 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.droidguard;
import android.content.Context;
import org.microg.gms.droidguard.DroidGuardClientImpl;
public class DroidGuard {
public static DroidGuardClient getClient(Context context) {
return new DroidGuardClientImpl(context);
}
public static DroidGuardClient getClient(Context context, String packageName) {
return new DroidGuardClientImpl(context, packageName);
}
}

View file

@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.droidguard;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest;
import com.google.android.gms.tasks.Task;
import java.util.Map;
public interface DroidGuardClient {
@NonNull Task<DroidGuardHandle> init(@NonNull String flow, @Nullable DroidGuardResultsRequest request);
@NonNull Task<String> getResults(@NonNull String flow, @Nullable Map<String, String> data, @Nullable DroidGuardResultsRequest request);
@NonNull
static Task<DroidGuardHandle> init(@NonNull Context context, @NonNull String flow) {
return DroidGuard.getClient(context).init(flow, null);
}
@NonNull
static Task<String> getResults(@NonNull Context context, @NonNull String flow, @Nullable Map<String, String> data) {
return getResults(context, flow, data, (DroidGuardResultsRequest) null);
}
@NonNull
static Task<String> getResults(@NonNull Context context, @NonNull String flow, @Nullable Map<String, String> data, @Nullable Bundle extras) {
DroidGuardResultsRequest request = null;
if (extras != null) {
request = new DroidGuardResultsRequest();
request.bundle.putAll(extras);
}
return getResults(context, flow, data, request);
}
@NonNull
static Task<String> getResults(@NonNull Context context, @NonNull String flow, @Nullable Map<String, String> data, @Nullable DroidGuardResultsRequest request) {
return DroidGuard.getClient(context).getResults(flow, data, request);
}
}

View file

@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.droidguard;
import java.util.Map;
public interface DroidGuardHandle {
String snapshot(Map<String, String> data);
boolean isOpened();
void close();
}

View file

@ -0,0 +1,50 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.droidguard.internal;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import androidx.annotation.Nullable;
public class DroidGuardInitReply implements Parcelable {
public @Nullable ParcelFileDescriptor pfd;
public @Nullable Parcelable object;
public DroidGuardInitReply(@Nullable ParcelFileDescriptor pfd, @Nullable Parcelable object) {
this.pfd = pfd;
this.object = object;
}
@Override
public int describeContents() {
return (pfd != null ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0) | (object != null ? object.describeContents() : 0);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(pfd, flags);
dest.writeParcelable(object, flags);
}
public final static Creator<DroidGuardInitReply> CREATOR = new Creator<DroidGuardInitReply>() {
@Override
public DroidGuardInitReply createFromParcel(Parcel source) {
ParcelFileDescriptor pfd = source.readParcelable(ParcelFileDescriptor.class.getClassLoader());
Parcelable object = source.readParcelable(getClass().getClassLoader());
if (pfd != null && object != null) {
return new DroidGuardInitReply(pfd, object);
}
return null;
}
@Override
public DroidGuardInitReply[] newArray(int size) {
return new DroidGuardInitReply[size];
}
};
}

View file

@ -0,0 +1,104 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.droidguard.internal;
import android.net.Network;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import org.microg.gms.common.Constants;
import org.microg.gms.utils.ToStringHelper;
import org.microg.safeparcel.AutoSafeParcelable;
public class DroidGuardResultsRequest extends AutoSafeParcelable {
private static final String KEY_APP_ARCHITECTURE = "appArchitecture";
private static final String KEY_CLIENT_VERSION = "clientVersion";
private static final String KEY_FD = "fd";
private static final String KEY_NETWORK_TO_USE = "networkToUse";
private static final String KEY_TIMEOUT_MS = "timeoutMs";
public static final String KEY_OPEN_HANDLES = "openHandles";
@Field(2)
public Bundle bundle;
public DroidGuardResultsRequest() {
bundle = new Bundle();
String arch;
try {
arch = System.getProperty("os.arch");
} catch (Exception ignored) {
arch = "?";
}
bundle.putString(KEY_APP_ARCHITECTURE, arch);
setClientVersion(Constants.GMS_VERSION_CODE);
}
public String getAppArchitecture() {
return bundle.getString(KEY_APP_ARCHITECTURE);
}
public int getTimeoutMillis() {
return bundle.getInt(KEY_TIMEOUT_MS, 60000);
}
public DroidGuardResultsRequest setTimeoutMillis(int millis) {
bundle.putInt(KEY_TIMEOUT_MS, millis);
return this;
}
public int getClientVersion() {
return bundle.getInt(KEY_CLIENT_VERSION);
}
public DroidGuardResultsRequest setClientVersion(int clientVersion) {
bundle.putInt(KEY_CLIENT_VERSION, clientVersion);
return this;
}
public ParcelFileDescriptor getFd() {
return bundle.getParcelable(KEY_FD);
}
public DroidGuardResultsRequest setFd(ParcelFileDescriptor fd) {
bundle.putParcelable(KEY_FD, fd);
return this;
}
public int getOpenHandles() {
return bundle.getInt(KEY_OPEN_HANDLES);
}
public DroidGuardResultsRequest setOpenHandles(int openHandles) {
bundle.putInt(KEY_OPEN_HANDLES, openHandles);
return this;
}
@RequiresApi(api = 21)
public Network getNetworkToUse() {
return bundle.getParcelable(KEY_NETWORK_TO_USE);
}
@RequiresApi(api = 21)
public DroidGuardResultsRequest setNetworkToUse(Network networkToUse) {
bundle.putParcelable(KEY_NETWORK_TO_USE, networkToUse);
return this;
}
@NonNull
@Override
public String toString() {
ToStringHelper helper = ToStringHelper.name("DroidGuardResultsRequest");
for (String key : bundle.keySet()) {
helper.field(key, bundle.get(key));
}
return helper.end();
}
public static final Creator<DroidGuardResultsRequest> CREATOR = new AutoCreator<>(DroidGuardResultsRequest.class);
}

View file

@ -0,0 +1,109 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import com.google.android.gms.droidguard.DroidGuardHandle;
import com.google.android.gms.droidguard.internal.DroidGuardInitReply;
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest;
import com.google.android.gms.droidguard.internal.IDroidGuardHandle;
import com.google.android.gms.droidguard.internal.IDroidGuardService;
import org.microg.gms.common.GmsClient;
import org.microg.gms.common.GmsService;
import com.google.android.gms.common.api.internal.ConnectionCallbacks;
import com.google.android.gms.common.api.internal.OnConnectionFailedListener;
public class DroidGuardApiClient extends GmsClient<IDroidGuardService> {
private static final String TAG = "DroidGuardApiClient";
private final Context context;
private int openHandles = 0;
private Handler handler;
private HandleProxyFactory factory;
public DroidGuardApiClient(Context context, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener) {
super(context, callbacks, connectionFailedListener, GmsService.DROIDGUARD.ACTION);
this.context = context;
serviceId = GmsService.DROIDGUARD.SERVICE_ID;
HandlerThread thread = new HandlerThread("DG");
thread.start();
handler = new Handler(thread.getLooper());
factory = new HandleProxyFactory(context);
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public DroidGuardHandle openHandle(String flow, DroidGuardResultsRequest request) {
try {
IDroidGuardHandle handle = getServiceInterface().getHandle();
request.setOpenHandles(openHandles);
DroidGuardInitReply reply = handle.initWithRequest(flow, request);
if (reply == null) {
handle.init(flow);
}
if (reply != null) {
if (reply.pfd != null && reply.object != null) {
Log.w(TAG, "DroidGuardInitReply suggests additional actions in main thread");
Bundle bundle = (Bundle) reply.object;
if (bundle != null) {
for (String key : bundle.keySet()) {
Log.d(TAG, "reply.object[" + key + "] = " + bundle.get(key));
}
handleDroidGuardData(reply.pfd, (Bundle) reply.object);
}
}
}
openHandles++;
return new DroidGuardHandleImpl(this, request, handle);
} catch (Exception e) {
return new DroidGuardHandleImpl(this, request, "Initialization failed: " + e);
}
}
private void handleDroidGuardData(ParcelFileDescriptor pfd, Bundle bundle) {
String vmKey = bundle.getString("h");
if (vmKey == null) {
throw new RuntimeException("Missing vmKey");
}
HandleProxy proxy = factory.createHandle(vmKey, pfd, bundle);
proxy.init();
proxy.close();
}
public void markHandleClosed() {
if (openHandles == 0) {
Log.w(TAG, "Can't mark handle closed if none is open");
return;
}
openHandles--;
if (openHandles == 0) disconnect();
}
public void runOnHandler(Runnable runnable) {
if (Looper.myLooper() == handler.getLooper()) {
runnable.run();
} else {
handler.post(runnable);
}
}
@Override
protected IDroidGuardService interfaceFromBinder(IBinder binder) {
return IDroidGuardService.Stub.asInterface(binder);
}
}

View file

@ -0,0 +1,63 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.GoogleApi;
import com.google.android.gms.droidguard.DroidGuardClient;
import com.google.android.gms.droidguard.DroidGuardHandle;
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest;
import com.google.android.gms.tasks.Task;
import org.microg.gms.common.api.ReturningGoogleApiCall;
import java.util.Map;
public class DroidGuardClientImpl extends GoogleApi<DroidGuardClientImpl.Options> implements DroidGuardClient {
private static final Api<Options> API = new Api<>((options, context, looper, clientSettings, callbacks, connectionFailedListener) -> {
DroidGuardApiClient client = new DroidGuardApiClient(context, callbacks, connectionFailedListener);
if (options != null && options.packageName != null) client.setPackageName(options.packageName);
return client;
});
public DroidGuardClientImpl(Context context) {
super(context, API, new Options(context.getPackageName()));
}
public DroidGuardClientImpl(Context context, String packageName) {
super(context, API, new Options(packageName));
}
@Override
@NonNull
public Task<DroidGuardHandle> init(@NonNull String flow, @Nullable DroidGuardResultsRequest request) {
DroidGuardResultsRequest finalRequest = request != null ? request : new DroidGuardResultsRequest();
return scheduleTask((ReturningGoogleApiCall<DroidGuardHandle, DroidGuardApiClient>) client -> client.openHandle(flow, finalRequest));
}
@Override
@NonNull
public Task<String> getResults(@NonNull String flow, @Nullable Map<String, String> data, @Nullable DroidGuardResultsRequest request) {
DroidGuardResultsRequest finalRequest = request != null ? request : new DroidGuardResultsRequest();
return scheduleTask((ReturningGoogleApiCall<String, DroidGuardApiClient>) client -> {
DroidGuardHandle handle = client.openHandle(flow, finalRequest);
String results = handle.snapshot(data);
handle.close();
return results;
});
}
public static class Options implements Api.ApiOptions.Optional {
public final String packageName;
public Options(String packageName) {
this.packageName = packageName;
}
}
}

View file

@ -0,0 +1,89 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard;
import android.util.Log;
import com.google.android.gms.droidguard.DroidGuardHandle;
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest;
import com.google.android.gms.droidguard.internal.IDroidGuardHandle;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class DroidGuardHandleImpl implements DroidGuardHandle {
private static final String TAG = "DroidGuardHandle";
private final DroidGuardApiClient apiClient;
private final DroidGuardResultsRequest request;
private IDroidGuardHandle handle;
private byte[] error;
public DroidGuardHandleImpl(DroidGuardApiClient apiClient, DroidGuardResultsRequest request, IDroidGuardHandle handle) {
this.apiClient = apiClient;
this.request = request;
this.handle = handle;
}
public DroidGuardHandleImpl(DroidGuardApiClient apiClient, DroidGuardResultsRequest request, String error) {
this.apiClient = apiClient;
this.request = request;
this.error = Utils.getErrorBytes(error);
}
@Override
public String snapshot(Map<String, String> data) {
byte[] result;
if (error != null) {
result = error;
} else {
ArrayBlockingQueue<byte[]> resultQueue = new ArrayBlockingQueue<>(1);
apiClient.runOnHandler(() -> {
byte[] innerResult;
try {
innerResult = handle.snapshot(data);
if (innerResult == null) {
error = Utils.getErrorBytes("Received null");
innerResult = error;
}
} catch (Exception e) {
error = Utils.getErrorBytes("Snapshot failed: " + e);
innerResult = error;
}
resultQueue.offer(innerResult);
});
try {
result = resultQueue.poll(request.getTimeoutMillis(), TimeUnit.MILLISECONDS);
if (result == null) {
result = Utils.getErrorBytes("Snapshot timeout: " + request.getTimeoutMillis() + " ms");
}
} catch (InterruptedException e) {
result = Utils.getErrorBytes("Results transfer failed: " + e);
}
}
return Utils.toBase64(result);
}
@Override
public boolean isOpened() {
return handle != null && error == null && handle.asBinder().pingBinder();
}
@Override
public void close() {
apiClient.runOnHandler(() -> {
if (handle != null) {
try {
handle.close();
} catch (Exception e) {
Log.w(TAG, "Error while closing handle.");
}
apiClient.markHandleClosed();
handle = null;
}
});
}
}

View file

@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard;
import android.util.Base64;
public class Utils {
public static byte[] getErrorBytes(String s) {
return ("ERROR : " + s).getBytes();
}
public static String toBase64(byte[] result) {
return Base64.encodeToString(result, Base64.URL_SAFE + Base64.NO_WRAP + Base64.NO_PADDING);
}
}

View file

@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: 2021, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard
class BytesException : Exception {
val bytes: ByteArray
constructor(bytes: ByteArray, message: String) : super(message) {
this.bytes = bytes
}
constructor(bytes: ByteArray, cause: Throwable) : super(cause) {
this.bytes = bytes
}
constructor(bytes: ByteArray, message: String, cause: Throwable) : super(message, cause) {
this.bytes = bytes
}
}

View file

@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard
import android.content.Context
import android.os.Bundle
import android.os.Parcelable
class HandleProxy(val handle: Any, val vmKey: String, val extra: ByteArray = ByteArray(0)) {
constructor(clazz: Class<*>, context: Context, vmKey: String, data: Parcelable) : this(
kotlin.runCatching {
clazz.getDeclaredConstructor(Context::class.java, Parcelable::class.java).newInstance(context, data)
}.getOrElse {
throw BytesException(ByteArray(0), it)
},
vmKey
)
constructor(clazz: Class<*>, context: Context, flow: String?, byteCode: ByteArray, callback: Any, vmKey: String, extra: ByteArray, bundle: Bundle?) : this(
kotlin.runCatching {
clazz.getDeclaredConstructor(Context::class.java, String::class.java, ByteArray::class.java, Object::class.java, Bundle::class.java).newInstance(context, flow, byteCode, callback, bundle)
}.getOrElse {
throw BytesException(extra, it)
}, vmKey, extra)
fun run(data: Map<Any, Any>): ByteArray {
try {
return handle.javaClass.getDeclaredMethod("run", Map::class.java).invoke(handle, data) as ByteArray
} catch (e: Exception) {
throw BytesException(extra, e)
}
}
fun init(): Boolean {
try {
return handle.javaClass.getDeclaredMethod("init").invoke(handle) as Boolean
} catch (e: Exception) {
throw BytesException(extra, e)
}
}
fun close() {
try {
handle.javaClass.getDeclaredMethod("close").invoke(handle)
} catch (e: Exception) {
throw BytesException(extra, e)
}
}
}

View file

@ -0,0 +1,117 @@
/*
* SPDX-FileCopyrightText: 2021 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard
import android.content.Context
import android.os.Bundle
import android.os.ParcelFileDescriptor
import android.os.Parcelable
import androidx.annotation.GuardedBy
import dalvik.system.DexClassLoader
import java.io.File
import java.io.IOException
import java.security.MessageDigest
import java.security.cert.Certificate
import java.util.*
open class HandleProxyFactory(private val context: Context) {
@GuardedBy("CLASS_LOCK")
protected val classMap = hashMapOf<String, Class<*>>()
fun createHandle(vmKey: String, pfd: ParcelFileDescriptor, extras: Bundle): HandleProxy {
fetchFromFileDescriptor(pfd, vmKey)
return createHandleProxy(vmKey, extras)
}
private fun fetchFromFileDescriptor(pfd: ParcelFileDescriptor, vmKey: String) {
if (!isValidCache(vmKey)) {
val auIs = ParcelFileDescriptor.AutoCloseInputStream(pfd)
val temp = File(getCacheDir(), "${UUID.randomUUID()}.apk")
temp.parentFile!!.mkdirs()
temp.writeBytes(auIs.readBytes())
auIs.close()
getOptDir(vmKey).mkdirs()
temp.renameTo(getTheApkFile(vmKey))
updateCacheTimestamp(vmKey)
if (!isValidCache(vmKey)) {
getCacheDir(vmKey).deleteRecursively()
throw IllegalStateException("Error ")
}
}
}
private fun createHandleProxy(
vmKey: String,
extras: Parcelable
): HandleProxy {
val clazz = loadClass(vmKey)
return HandleProxy(clazz, context, vmKey, extras)
}
fun getTheApkFile(vmKey: String) = File(getCacheDir(vmKey), "the.apk")
protected fun getCacheDir() = context.getDir(CACHE_FOLDER_NAME, Context.MODE_PRIVATE)
protected fun getCacheDir(vmKey: String) = File(getCacheDir(), vmKey)
protected fun getOptDir(vmKey: String) = File(getCacheDir(vmKey), "opt")
protected fun isValidCache(vmKey: String) = getTheApkFile(vmKey).isFile && getOptDir(vmKey).isDirectory
protected fun updateCacheTimestamp(vmKey: String) {
try {
val timestampFile = File(getCacheDir(vmKey), "t")
if (!timestampFile.exists() && !timestampFile.createNewFile()) {
throw Exception("Failed to touch last-used file for $vmKey.")
}
if (!timestampFile.setLastModified(System.currentTimeMillis())) {
throw Exception("Failed to update last-used timestamp for $vmKey.")
}
} catch (e: IOException) {
throw Exception("Failed to touch last-used file for $vmKey.")
}
}
private fun verifyApkSignature(apk: File): Boolean {
return true
val certificates: Array<Certificate> = TODO()
if (certificates.size != 1) return false
return Arrays.equals(MessageDigest.getInstance("SHA-256").digest(certificates[0].encoded), PROD_CERT_HASH)
}
protected fun loadClass(vmKey: String, bytes: ByteArray = ByteArray(0)): Class<*> {
synchronized(CLASS_LOCK) {
val cachedClass = classMap[vmKey]
if (cachedClass != null) {
updateCacheTimestamp(vmKey)
return cachedClass
}
val weakClass = weakClassMap[vmKey]
if (weakClass != null) {
classMap[vmKey] = weakClass
updateCacheTimestamp(vmKey)
return weakClass
}
if (!isValidCache(vmKey)) {
throw BytesException(bytes, "VM key $vmKey not found in cache")
}
if (!verifyApkSignature(getTheApkFile(vmKey))) {
getCacheDir(vmKey).deleteRecursively()
throw ClassNotFoundException("APK signature verification failed")
}
val loader = DexClassLoader(getTheApkFile(vmKey).absolutePath, getOptDir(vmKey).absolutePath, null, context.classLoader)
val clazz = loader.loadClass(CLASS_NAME)
classMap[vmKey] = clazz
weakClassMap[vmKey] = clazz
return clazz
}
}
companion object {
const val CLASS_NAME = "com.google.ccc.abuse.droidguard.DroidGuard"
const val CACHE_FOLDER_NAME = "cache_dg"
val CLASS_LOCK = Object()
@GuardedBy("CLASS_LOCK")
val weakClassMap = WeakHashMap<String, Class<*>>()
val PROD_CERT_HASH = byteArrayOf(61, 122, 18, 35, 1, -102, -93, -99, -98, -96, -29, 67, 106, -73, -64, -119, 107, -5, 79, -74, 121, -12, -34, 95, -25, -62, 63, 50, 108, -113, -103, 74)
}
}