Repo Created
This commit is contained in:
parent
eb305e2886
commit
a8c22c65db
4784 changed files with 329907 additions and 2 deletions
42
play-services-auth-blockstore/core/build.gradle
Normal file
42
play-services-auth-blockstore/core/build.gradle
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
dependencies {
|
||||
api project(':play-services-auth-blockstore')
|
||||
implementation project(':play-services-base-core')
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "org.microg.gms.auth.blockstore"
|
||||
|
||||
compileSdkVersion androidCompileSdk
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
|
||||
defaultConfig {
|
||||
versionName version
|
||||
minSdkVersion androidMinSdk
|
||||
targetSdkVersion androidTargetSdk
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = 1.8
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'MissingTranslation', 'GetLocales'
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ SPDX-FileCopyrightText: 2025 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">
|
||||
|
||||
<application>
|
||||
|
||||
<service android:name="org.microg.gms.auth.blockstore.BlockstoreApiService">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.gms.auth.blockstore.service.START" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.blockstore
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.google.android.gms.auth.blockstore.BlockstoreClient
|
||||
import com.google.android.gms.auth.blockstore.BlockstoreStatusCodes
|
||||
import com.google.android.gms.auth.blockstore.DeleteBytesRequest
|
||||
import com.google.android.gms.auth.blockstore.RetrieveBytesRequest
|
||||
import com.google.android.gms.auth.blockstore.RetrieveBytesResponse
|
||||
import com.google.android.gms.auth.blockstore.StoreBytesData
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.microg.gms.utils.toBase64
|
||||
|
||||
private const val SHARED_PREFS_NAME = "com.google.android.gms.blockstore"
|
||||
|
||||
private const val TAG = "BlockStoreImpl"
|
||||
|
||||
class BlockStoreImpl(context: Context, val callerPackage: String) {
|
||||
|
||||
private val blockStoreSp: SharedPreferences by lazy {
|
||||
context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
private fun initSpByPackage(): Map<String, *>? {
|
||||
val map = blockStoreSp.all
|
||||
if (map.isNullOrEmpty() || map.all { !it.key.startsWith(callerPackage) }) return null
|
||||
return map.filter { it.key.startsWith(callerPackage) }
|
||||
}
|
||||
|
||||
suspend fun deleteBytesWithRequest(request: DeleteBytesRequest?): Boolean = withContext(Dispatchers.IO) {
|
||||
Log.d(TAG, "deleteBytesWithRequest: callerPackage: $callerPackage")
|
||||
val localData = initSpByPackage()
|
||||
if (request == null || localData.isNullOrEmpty()) return@withContext false
|
||||
if (request.deleteAll) {
|
||||
localData.keys.forEach { blockStoreSp.edit()?.remove(it)?.commit() }
|
||||
} else {
|
||||
request.keys.forEach { blockStoreSp.edit()?.remove("$callerPackage:$it")?.commit() }
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
suspend fun retrieveBytesWithRequest(request: RetrieveBytesRequest?): RetrieveBytesResponse? = withContext(Dispatchers.IO) {
|
||||
Log.d(TAG, "retrieveBytesWithRequest: callerPackage: $callerPackage")
|
||||
val localData = initSpByPackage()
|
||||
if (request == null || localData.isNullOrEmpty()) return@withContext null
|
||||
val data = mutableListOf<RetrieveBytesResponse.BlockstoreData>()
|
||||
val filterKeys = if (request.keys.isNullOrEmpty()) emptyList<String>() else request.keys
|
||||
for (key in localData.keys) {
|
||||
val bytesKey = key.substring(callerPackage.length + 1)
|
||||
if (filterKeys.isNotEmpty() && !filterKeys.contains(bytesKey)) continue
|
||||
val bytes = blockStoreSp.getString(key, null)?.let { Base64.decode(it, Base64.URL_SAFE) } ?: continue
|
||||
data.add(RetrieveBytesResponse.BlockstoreData(bytes, bytesKey))
|
||||
}
|
||||
RetrieveBytesResponse(Bundle.EMPTY, data)
|
||||
}
|
||||
|
||||
suspend fun retrieveBytes(): ByteArray? = withContext(Dispatchers.IO) {
|
||||
Log.d(TAG, "retrieveBytes: callerPackage: $callerPackage")
|
||||
val localData = initSpByPackage()
|
||||
if (localData.isNullOrEmpty()) return@withContext null
|
||||
val savedKey = localData.keys.firstOrNull { it == "$callerPackage:${BlockstoreClient.DEFAULT_BYTES_DATA_KEY}" } ?: return@withContext null
|
||||
blockStoreSp.getString(savedKey, null)?.let { Base64.decode(it, Base64.URL_SAFE) }
|
||||
}
|
||||
|
||||
suspend fun storeBytes(data: StoreBytesData?): Int = withContext(Dispatchers.IO) {
|
||||
if (data == null || data.bytes == null) return@withContext 0
|
||||
val localData = initSpByPackage()
|
||||
if ((localData?.size ?: 0) >= BlockstoreClient.MAX_ENTRY_COUNT) {
|
||||
return@withContext BlockstoreStatusCodes.TOO_MANY_ENTRIES
|
||||
}
|
||||
val bytes = data.bytes
|
||||
if (bytes.size > BlockstoreClient.MAX_SIZE) {
|
||||
return@withContext BlockstoreStatusCodes.MAX_SIZE_EXCEEDED
|
||||
}
|
||||
val savedKey = "$callerPackage:${data.key ?: BlockstoreClient.DEFAULT_BYTES_DATA_KEY}"
|
||||
val base64 = bytes.toBase64(Base64.URL_SAFE)
|
||||
val bool = blockStoreSp.edit()?.putString(savedKey, base64)?.commit()
|
||||
if (bool == true) bytes.size else 0
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.blockstore
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.gms.auth.blockstore.AppRestoreInfo
|
||||
import com.google.android.gms.auth.blockstore.BlockstoreStatusCodes
|
||||
import com.google.android.gms.auth.blockstore.DeleteBytesRequest
|
||||
import com.google.android.gms.auth.blockstore.RetrieveBytesRequest
|
||||
import com.google.android.gms.auth.blockstore.RetrieveBytesResponse
|
||||
import com.google.android.gms.auth.blockstore.StoreBytesData
|
||||
import com.google.android.gms.auth.blockstore.internal.IBlockstoreService
|
||||
import com.google.android.gms.auth.blockstore.internal.IDeleteBytesCallback
|
||||
import com.google.android.gms.auth.blockstore.internal.IGetAccessForPackageCallback
|
||||
import com.google.android.gms.auth.blockstore.internal.IGetBlockstoreDataCallback
|
||||
import com.google.android.gms.auth.blockstore.internal.IIsEndToEndEncryptionAvailableCallback
|
||||
import com.google.android.gms.auth.blockstore.internal.IRetrieveBytesCallback
|
||||
import com.google.android.gms.auth.blockstore.internal.ISetBlockstoreDataCallback
|
||||
import com.google.android.gms.auth.blockstore.internal.IStoreBytesCallback
|
||||
import com.google.android.gms.common.Feature
|
||||
import com.google.android.gms.common.api.CommonStatusCodes
|
||||
import com.google.android.gms.common.api.Status
|
||||
import com.google.android.gms.common.api.internal.IStatusCallback
|
||||
import com.google.android.gms.common.internal.ConnectionInfo
|
||||
import com.google.android.gms.common.internal.GetServiceRequest
|
||||
import com.google.android.gms.common.internal.IGmsCallbacks
|
||||
import kotlinx.coroutines.launch
|
||||
import org.microg.gms.BaseService
|
||||
import org.microg.gms.common.GmsService
|
||||
import org.microg.gms.common.GmsService.BLOCK_STORE
|
||||
import org.microg.gms.common.PackageUtils
|
||||
|
||||
private const val TAG = "BlockstoreApiService"
|
||||
|
||||
private val FEATURES = arrayOf(
|
||||
Feature("auth_blockstore", 3),
|
||||
Feature("blockstore_data_transfer", 1),
|
||||
Feature("blockstore_notify_app_restore", 1),
|
||||
Feature("blockstore_store_bytes_with_options", 2),
|
||||
Feature("blockstore_is_end_to_end_encryption_available", 1),
|
||||
Feature("blockstore_enable_cloud_backup", 1),
|
||||
Feature("blockstore_delete_bytes", 2),
|
||||
Feature("blockstore_retrieve_bytes_with_options", 3),
|
||||
Feature("auth_clear_restore_credential", 2),
|
||||
Feature("auth_create_restore_credential", 1),
|
||||
Feature("auth_get_restore_credential", 1),
|
||||
Feature("auth_get_private_restore_credential_key", 1),
|
||||
Feature("auth_set_private_restore_credential_key", 1),
|
||||
)
|
||||
|
||||
class BlockstoreApiService : BaseService(TAG, BLOCK_STORE) {
|
||||
|
||||
override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) {
|
||||
try {
|
||||
val packageName = PackageUtils.getAndCheckCallingPackage(this, request.packageName) ?: throw IllegalArgumentException("Missing package name")
|
||||
|
||||
val blockStoreImpl = BlockStoreImpl(this, packageName)
|
||||
callback.onPostInitCompleteWithConnectionInfo(
|
||||
CommonStatusCodes.SUCCESS, BlobstoreServiceImpl(blockStoreImpl, lifecycle).asBinder(), ConnectionInfo().apply { features = FEATURES })
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "handleServiceRequest", e)
|
||||
callback.onPostInitComplete(CommonStatusCodes.INTERNAL_ERROR, null, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BlobstoreServiceImpl(val blockStore: BlockStoreImpl, override val lifecycle: Lifecycle) : IBlockstoreService.Stub(), LifecycleOwner {
|
||||
|
||||
override fun retrieveBytes(callback: IRetrieveBytesCallback?) {
|
||||
Log.d(TAG, "Method (retrieveBytes) called")
|
||||
lifecycleScope.launch {
|
||||
runCatching {
|
||||
val retrieveBytes = blockStore.retrieveBytes()
|
||||
if (retrieveBytes != null) {
|
||||
callback?.onBytesResult(Status.SUCCESS, retrieveBytes)
|
||||
} else {
|
||||
callback?.onBytesResult(Status.INTERNAL_ERROR, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setBlockstoreData(callback: ISetBlockstoreDataCallback?, data: ByteArray?) {
|
||||
Log.d(TAG, "Method (setBlockstoreData: ${data?.size}) called but not implemented")
|
||||
}
|
||||
|
||||
override fun getBlockstoreData(callback: IGetBlockstoreDataCallback?) {
|
||||
Log.d(TAG, "Method (getBlockstoreData) called but not implemented")
|
||||
}
|
||||
|
||||
override fun getAccessForPackage(callback: IGetAccessForPackageCallback?, packageName: String?) {
|
||||
Log.d(TAG, "Method (getAccessForPackage: $packageName) called but not implemented")
|
||||
}
|
||||
|
||||
override fun setFlagWithPackage(callback: IStatusCallback?, packageName: String?, flag: Int) {
|
||||
Log.d(TAG, "Method (setFlagWithPackage: $packageName, $flag) called but not implemented")
|
||||
}
|
||||
|
||||
override fun clearFlagForPackage(callback: IStatusCallback?, packageName: String?) {
|
||||
Log.d(TAG, "Method (clearFlagForPackage: $packageName) called but not implemented")
|
||||
}
|
||||
|
||||
override fun updateFlagForPackage(callback: IStatusCallback?, packageName: String?, value: Int) {
|
||||
Log.d(TAG, "Method (updateFlagForPackage: $packageName, $value) called but not implemented")
|
||||
}
|
||||
|
||||
override fun reportAppRestore(callback: IStatusCallback?, packages: List<String?>?, code: Int, info: AppRestoreInfo?) {
|
||||
Log.d(TAG, "Method (reportAppRestore: $packages, $code, $info) called but not implemented")
|
||||
}
|
||||
|
||||
override fun storeBytes(callback: IStoreBytesCallback?, data: StoreBytesData?) {
|
||||
Log.d(TAG, "Method (storeBytes: $data) called")
|
||||
lifecycleScope.launch {
|
||||
runCatching {
|
||||
val storeBytes = blockStore.storeBytes(data)
|
||||
Log.d(TAG, "storeBytes: size: $storeBytes")
|
||||
when (storeBytes) {
|
||||
0 -> callback?.onStoreBytesResult(Status.INTERNAL_ERROR, BlockstoreStatusCodes.FEATURE_NOT_SUPPORTED)
|
||||
BlockstoreStatusCodes.MAX_SIZE_EXCEEDED -> callback?.onStoreBytesResult(Status.INTERNAL_ERROR, BlockstoreStatusCodes.MAX_SIZE_EXCEEDED)
|
||||
BlockstoreStatusCodes.TOO_MANY_ENTRIES -> callback?.onStoreBytesResult(Status.INTERNAL_ERROR, BlockstoreStatusCodes.TOO_MANY_ENTRIES)
|
||||
else -> callback?.onStoreBytesResult(Status.SUCCESS, storeBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isEndToEndEncryptionAvailable(callback: IIsEndToEndEncryptionAvailableCallback?) {
|
||||
Log.d(TAG, "Method (isEndToEndEncryptionAvailable) called")
|
||||
runCatching { callback?.onCheckEndToEndEncryptionResult(Status.SUCCESS, false) }
|
||||
}
|
||||
|
||||
override fun retrieveBytesWithRequest(callback: IRetrieveBytesCallback?, request: RetrieveBytesRequest?) {
|
||||
Log.d(TAG, "Method (retrieveBytesWithRequest: $request) called")
|
||||
lifecycleScope.launch {
|
||||
runCatching {
|
||||
val retrieveBytesResponse = blockStore.retrieveBytesWithRequest(request)
|
||||
Log.d(TAG, "retrieveBytesWithRequest: retrieveBytesResponse: $retrieveBytesResponse")
|
||||
if (retrieveBytesResponse != null) {
|
||||
callback?.onResponseResult(Status.SUCCESS, retrieveBytesResponse)
|
||||
} else {
|
||||
callback?.onResponseResult(Status.INTERNAL_ERROR, RetrieveBytesResponse(Bundle.EMPTY, emptyList()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun deleteBytes(callback: IDeleteBytesCallback?, request: DeleteBytesRequest?) {
|
||||
Log.d(TAG, "Method (deleteBytes: $request) called")
|
||||
lifecycleScope.launch {
|
||||
runCatching {
|
||||
val deleted = blockStore.deleteBytesWithRequest(request)
|
||||
callback?.onDeleteBytesResult(Status.SUCCESS, deleted)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue