Repo Created
This commit is contained in:
parent
eb305e2886
commit
a8c22c65db
4784 changed files with 329907 additions and 2 deletions
49
play-services-droidguard/build.gradle
Normal file
49
play-services-droidguard/build.gradle
Normal 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"
|
||||
}
|
||||
72
play-services-droidguard/core/build.gradle
Normal file
72
play-services-droidguard/core/build.gradle
Normal 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'
|
||||
27
play-services-droidguard/core/src/main/AndroidManifest.xml
Normal file
27
play-services-droidguard/core/src/main/AndroidManifest.xml
Normal 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>
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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>())
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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 l’accès à l’attestation matérielle quand DroidGuard embarqué est utilisé. Peut aider à passer les vérifications d\'intégrité sur certains appareils.</string>
|
||||
</resources>
|
||||
|
|
@ -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í. D’fhéadfadh sé seo cabhrú le seiceálacha sláine a rith ar roinnt gléasanna.</string>
|
||||
</resources>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
</resources>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
7
play-services-droidguard/src/main/AndroidManifest.xml
Normal file
7
play-services-droidguard/src/main/AndroidManifest.xml
Normal 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 />
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.droidguard.internal;
|
||||
|
||||
parcelable DroidGuardInitReply;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.droidguard.internal;
|
||||
|
||||
parcelable DroidGuardResultsRequest;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.google.android.gms.droidguard.internal;
|
||||
|
||||
interface IDroidGuardCallbacks {
|
||||
void onResult(in byte[] res);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue