Repo Created

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

View file

@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
apply plugin: 'signing'
android {
namespace "com.google.android.gms.auth.api.phone"
compileSdkVersion androidCompileSdk
buildToolsVersion "$androidBuildVersionTools"
buildFeatures {
aidl = true
}
defaultConfig {
versionName version
minSdkVersion androidMinSdk
targetSdkVersion androidTargetSdk
}
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
}
apply from: '../gradle/publish-android.gradle'
description = 'microG implementation of play-services-auth-api-phone'
dependencies {
// Dependencies from play-services-auth-api-phone:18.2.0
api project(':play-services-base')
api project(':play-services-basement')
api project(':play-services-tasks')
api 'org.jetbrains.kotlin:kotlin-stdlib:1.8.21'
api 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.21'
api 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3'
// api 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.0'
}

View file

@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
dependencies {
api project(':play-services-auth-api-phone')
implementation project(':play-services-base-core')
implementation "androidx.appcompat:appcompat:$appcompatVersion"
}
android {
namespace "org.microg.gms.auth.phone"
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'
}
}

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ SPDX-FileCopyrightText: 2023 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.RECEIVE_SMS" />
<application>
<activity
android:name="org.microg.gms.auth.phone.UserConsentPromptActivity"
android:exported="true"
android:process=":ui"
android:theme="@style/Theme.AppCompat.DayNight.Dialog.Alert.NoActionBar" />
<activity
android:name="org.microg.gms.auth.phone.AskPermissionActivity"
android:excludeFromRecents="true"
android:exported="false"
android:process=":ui"
android:theme="@style/Theme.Translucent" />
<service
android:name="org.microg.gms.auth.phone.SmsRetrieverService"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.phone.service.SmsRetrieverApiService.START" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
</manifest>

View file

@ -0,0 +1,54 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.auth.phone
import android.Manifest
import android.content.Intent
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import android.os.Message
import android.os.Messenger
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.os.bundleOf
private const val TAG = "AskPermission"
private const val REQUEST_CODE_PERMISSION = 101
private val ALLOWED_PERMISSIONS = setOf(Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_CONTACTS)
class AskPermissionActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val permissions = intent.getStringArrayExtra(EXTRA_PERMISSIONS) ?: arrayOf(Manifest.permission.RECEIVE_SMS)
Log.d(TAG, "Requesting permissions: ${permissions.toList()}")
if (SDK_INT < 23 || permissions.any { it !in ALLOWED_PERMISSIONS }) {
sendReply(RESULT_CANCELED)
finish()
} else {
ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE_PERMISSION)
}
}
private fun sendReply(code: Int = RESULT_OK, extras: Bundle = Bundle.EMPTY) {
intent.getParcelableExtra<Messenger>(EXTRA_MESSENGER)?.let {
it.send(Message.obtain().apply {
what = code
data = extras
})
}
setResult(code, Intent().apply { putExtras(extras) })
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
if (requestCode == REQUEST_CODE_PERMISSION) {
sendReply(extras = bundleOf(EXTRA_GRANT_RESULTS to grantResults))
finish()
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
}

View file

@ -0,0 +1,359 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.auth.phone
import android.Manifest.permission.READ_CONTACTS
import android.Manifest.permission.RECEIVE_SMS
import android.annotation.TargetApi
import android.app.Activity
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.database.Cursor
import android.os.*
import android.os.Build.VERSION.SDK_INT
import android.provider.ContactsContract
import android.provider.ContactsContract.CommonDataKinds.Phone
import android.provider.Telephony
import android.telephony.SmsMessage
import android.text.TextUtils
import android.util.Base64
import android.util.Log
import androidx.core.app.PendingIntentCompat
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import com.google.android.gms.auth.api.phone.SmsRetriever
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.Status
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.microg.gms.auth.phone.SmsRetrieverRequestType.RETRIEVER
import org.microg.gms.auth.phone.SmsRetrieverRequestType.USER_CONSENT
import org.microg.gms.common.Constants
import org.microg.gms.utils.getSignatures
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.util.concurrent.atomic.AtomicInteger
private const val TAG = "SmsRetrieverCore"
private const val ACTION_SMS_RETRIEVE_TIMEOUT = "org.microg.gms.auth.phone.ACTION_SMS_RETRIEVE_TIMEOUT"
private const val EXTRA_REQUEST_ID = "requestId"
private const val TIMEOUT = 1000 * 60 * 5 // 5 minutes
private const val MESSAGE_MAX_LEN = 140
class SmsRetrieverCore(private val context: Context, override val lifecycle: Lifecycle) : LifecycleOwner, DefaultLifecycleObserver {
private val requests: HashMap<Int, SmsRetrieverRequest> = hashMapOf()
private val requestIdCounter = AtomicInteger(0)
private lateinit var timeoutBroadcastReceiver: BroadcastReceiver
private lateinit var smsBroadcastReceiver: BroadcastReceiver
private var requestCode = 0
private val alarmManager: AlarmManager
get() = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
init {
lifecycle.addObserver(this)
}
@TargetApi(19)
private fun configureBroadcastListenersIfNeeded() {
synchronized(this) {
if (!this::timeoutBroadcastReceiver.isInitialized) {
val intentFilter = IntentFilter(ACTION_SMS_RETRIEVE_TIMEOUT)
timeoutBroadcastReceiver = TimeoutReceiver()
ContextCompat.registerReceiver(context, timeoutBroadcastReceiver, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED)
}
if (!this::smsBroadcastReceiver.isInitialized) {
val intentFilter = IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)
intentFilter.priority = 999
smsBroadcastReceiver = SmsReceiver()
context.registerReceiver(smsBroadcastReceiver, intentFilter)
}
}
}
private suspend fun ensureReady(permissions: Array<String>): Boolean {
if (SDK_INT < 19) throw RuntimeException("Version not supported")
if (!ensurePermission(permissions)) return false
configureBroadcastListenersIfNeeded()
return true
}
suspend fun startSmsRetriever(packageName: String) {
val appHashString = getHashString(packageName)
if (!ensureReady(arrayOf(RECEIVE_SMS)))
throw RuntimeException("Initialization failed")
if (anyOtherPackageHasHashString(packageName, appHashString))
throw RuntimeException("Collision in hash string, can't use SMS Retriever API")
if (requests.values.any { it.packageName == packageName && it.appHashString == appHashString && it.type == RETRIEVER })
throw RuntimeException("App already listening")
val request = SmsRetrieverRequest(
id = requestIdCounter.incrementAndGet(),
type = RETRIEVER,
packageName = packageName,
appHashString = appHashString,
timeoutPendingIntent = getTimeoutPendingIntent(context, packageName)
)
requests[request.id] = request
alarmManager.set(AlarmManager.RTC, request.creation + TIMEOUT, request.timeoutPendingIntent)
}
suspend fun startWithConsentPrompt(packageName: String, senderPhoneNumber: String?) {
if (!ensureReady(arrayOf(RECEIVE_SMS, READ_CONTACTS)))
throw RuntimeException("Initialization failed")
if (requests.values.any { it.packageName == packageName && it.senderPhoneNumber == senderPhoneNumber && it.type == USER_CONSENT })
throw RuntimeException("App already listening")
val request = SmsRetrieverRequest(
id = requestIdCounter.incrementAndGet(),
type = USER_CONSENT,
packageName = packageName,
senderPhoneNumber = senderPhoneNumber,
timeoutPendingIntent = getTimeoutPendingIntent(context, packageName)
)
requests[request.id] = request
alarmManager.set(AlarmManager.RTC, request.creation + TIMEOUT, request.timeoutPendingIntent)
}
fun hasOngoingUserConsentRequest(): Boolean {
return requests.values.any { it.type == USER_CONSENT }
}
private fun sendRetrieverBroadcast(request: SmsRetrieverRequest, messageBody: String) {
sendReply(request, Status.SUCCESS, bundleOf(SmsRetriever.EXTRA_SMS_MESSAGE to messageBody))
}
private fun sendUserConsentBroadcast(request: SmsRetrieverRequest, messageBody: String) {
val userConsentIntent = Intent(context, UserConsentPromptActivity::class.java)
userConsentIntent.setPackage(Constants.GMS_PACKAGE_NAME)
userConsentIntent.putExtra(EXTRA_MESSENGER, Messenger(object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
if (Binder.getCallingUid() == Process.myUid()) {
if (msg.what == MSG_REQUEST_MESSAGE_BODY) {
msg.replyTo?.send(Message.obtain().apply {
what = 1
data = bundleOf("message" to messageBody)
})
} else if (msg.what == MSG_CONSUME_MESSAGE) {
finishRequest(request)
}
}
}
}))
sendReply(request, Status.SUCCESS, bundleOf(SmsRetriever.EXTRA_CONSENT_INTENT to userConsentIntent), false)
}
private fun getTimeoutPendingIntent(context: Context, packageName: String): PendingIntent {
val intent = Intent(ACTION_SMS_RETRIEVE_TIMEOUT)
intent.setPackage(packageName)
return PendingIntentCompat.getBroadcast(context, ++requestCode, intent, 0, false)!!
}
private fun tryHandleIncomingMessageAsRetrieverMessage(messageBody: String): Boolean {
for (request in requests.values) {
if (request.type == RETRIEVER) {
// 11-digit hash code that uniquely identifies your app
if (request.appHashString.isNullOrBlank() || !messageBody.contains(request.appHashString)) continue
sendRetrieverBroadcast(request, messageBody)
return true
}
}
return false
}
private fun tryHandleIncomingMessageAsUserConsentMessage(senderPhoneNumber: String?, messageBody: String): Boolean {
val senderPhoneNumber = senderPhoneNumber ?: return false
// 4-10 digit alphanumeric code containing at least one number
if (messageBody.split("[^A-Za-z0-9]".toRegex()).none { it.length in 4..10 && it.any(Char::isDigit) }) return false
// Sender cannot be in the user's Contacts list
if (isPhoneNumberInContacts(context, senderPhoneNumber)) return false
for (request in requests.values) {
if (request.type == USER_CONSENT) {
if (!request.senderPhoneNumber.isNullOrBlank() && request.senderPhoneNumber != senderPhoneNumber) continue
sendUserConsentBroadcast(request, messageBody)
return true
}
}
return false
}
private fun handleIncomingSmsMessage(senderPhoneNumber: String?, messageBody: String) {
Log.d(TAG, "handleIncomingSmsMessage: senderPhoneNumber:$senderPhoneNumber messageBody: $messageBody")
if (messageBody.isBlank()) return
if (tryHandleIncomingMessageAsRetrieverMessage(messageBody)) return
if (tryHandleIncomingMessageAsUserConsentMessage(senderPhoneNumber, messageBody)) return
}
fun handleTimeout(requestId: Int) {
val request = requests[requestId] ?: return
sendReply(request, Status(CommonStatusCodes.TIMEOUT))
}
private fun sendReply(request: SmsRetrieverRequest, status: Status, extras: Bundle = Bundle.EMPTY, finish: Boolean = true) {
Log.d(TAG, "Send reply to ${request.packageName} ${CommonStatusCodes.getStatusCodeString(status.statusCode)}")
val intent = Intent(SmsRetriever.SMS_RETRIEVED_ACTION)
intent.setPackage(request.packageName)
intent.putExtras(extras)
intent.putExtra(SmsRetriever.EXTRA_STATUS, status)
context.sendBroadcast(intent)
if (finish) finishRequest(request)
}
fun finishRequest(request: SmsRetrieverRequest) {
alarmManager.cancel(request.timeoutPendingIntent)
requests.remove(request.id)
}
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
if (this::smsBroadcastReceiver.isInitialized) context.unregisterReceiver(smsBroadcastReceiver)
if (this::timeoutBroadcastReceiver.isInitialized) context.unregisterReceiver(timeoutBroadcastReceiver)
for (request in requests.values) {
sendReply(request, Status(CommonStatusCodes.TIMEOUT))
}
requests.clear()
}
@TargetApi(19)
private inner class SmsReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION == intent.action) {
val messages = Telephony.Sms.Intents.getMessagesFromIntent(intent)
val messageBodyBuilder = StringBuilder()
var senderPhoneNumber: String? = null
for (message in messages) {
messageBodyBuilder.append(message.messageBody)
senderPhoneNumber = message.originatingAddress
}
try {
handleIncomingSmsMessage(senderPhoneNumber, messageBodyBuilder.toString())
} catch (e: Exception) {
Log.w(TAG, "Error handling incoming SMS", e)
}
}
}
}
private inner class TimeoutReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val requestId = intent.getIntExtra(EXTRA_REQUEST_ID, -1)
if (requestId != -1) {
handleTimeout(requestId)
}
}
}
@TargetApi(19)
fun getHashString(packageName: String): String {
val signature =
context.packageManager.getSignatures(packageName).firstOrNull()?.toCharsString() ?: throw RuntimeException("No signature found for $packageName")
val appInfo = "$packageName $signature"
val messageDigest = MessageDigest.getInstance("SHA-256")
messageDigest.update(appInfo.toByteArray(StandardCharsets.UTF_8))
return Base64.encodeToString(messageDigest.digest(), Base64.NO_PADDING or Base64.NO_WRAP).substring(0, 11)
}
private fun anyOtherPackageHasHashString(packageName: String, hashString: String): Boolean {
val collision = context.packageManager.getInstalledPackages(0)
.firstOrNull { it.packageName != packageName && getHashString(it.packageName) == hashString } ?: return false
Log.w(TAG, "Hash string collision between $packageName and ${collision.packageName} (both are $hashString)")
return true
}
private fun isPhoneNumberInContacts(context: Context, phoneNumber: String): Boolean {
fun normalizePhoneNumber(input: String): String {
var output = ""
if (!TextUtils.isEmpty(input)) {
// only keep digits
val temp = input.replace("[^0-9]".toRegex(), "")
// trim leading zeroes
output = temp.replaceFirst("^0*".toRegex(), "")
}
return output
}
val normalizePhoneNumber = normalizePhoneNumber(phoneNumber)
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(Phone.CONTENT_URI, arrayOf(Phone.NUMBER), null, null, null) ?: return false
while (cursor.moveToNext()) {
val addressIndex = cursor.getColumnIndex(Phone.NUMBER)
val contactPhoneNumber = normalizePhoneNumber(cursor.getString(addressIndex))
if (!TextUtils.isEmpty(normalizePhoneNumber) && !TextUtils.isEmpty(contactPhoneNumber) && normalizePhoneNumber == contactPhoneNumber) {
return true
}
}
} catch (e: Exception) {
Log.w(TAG, e)
} finally {
cursor?.close()
}
return false
}
private val activePermissionRequestLock = Mutex()
private var activePermissionRequest: Deferred<Boolean>? = null
private suspend fun ensurePermission(permissions: Array<String>): Boolean {
if (SDK_INT < 23)
return true
if (permissions.all { ContextCompat.checkSelfPermission(context, it) == PERMISSION_GRANTED })
return true
val (completable, deferred) = activePermissionRequestLock.withLock {
if (activePermissionRequest == null) {
val completable = CompletableDeferred<Boolean>()
activePermissionRequest = completable
completable to activePermissionRequest!!
} else {
null to activePermissionRequest!!
}
}
if (completable != null) {
val intent = Intent(context, AskPermissionActivity::class.java)
intent.putExtra(EXTRA_MESSENGER, Messenger(object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
if (msg.what == Activity.RESULT_OK) {
val grantResults = msg.data?.getIntArray(EXTRA_GRANT_RESULTS) ?: IntArray(0)
completable.complete(grantResults.size == permissions.size && grantResults.all { it == PERMISSION_GRANTED })
} else {
completable.complete(false)
}
}
}))
intent.putExtra(EXTRA_PERMISSIONS, permissions)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent)
}
return deferred.await()
}
}

View file

@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.auth.phone
import android.app.PendingIntent
data class SmsRetrieverRequest(
val id: Int,
val type: SmsRetrieverRequestType,
val packageName: String,
val timeoutPendingIntent: PendingIntent,
val appHashString: String? = null,
val creation: Long = System.currentTimeMillis(),
val senderPhoneNumber: String? = null
)
enum class SmsRetrieverRequestType {
RETRIEVER, USER_CONSENT
}

View file

@ -0,0 +1,131 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.auth.phone
import android.util.Log
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.auth.api.phone.SmsRetrieverStatusCodes
import com.google.android.gms.auth.api.phone.internal.IAutofillPermissionStateCallback
import com.google.android.gms.auth.api.phone.internal.IOngoingSmsRequestCallback
import com.google.android.gms.auth.api.phone.internal.ISmsRetrieverApiService
import com.google.android.gms.auth.api.phone.internal.ISmsRetrieverResultCallback
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 org.microg.gms.BaseService
import org.microg.gms.common.GmsService
import org.microg.gms.common.PackageUtils
private const val TAG = "SmsRetrieverService"
private val FEATURES = arrayOf(
Feature("sms_retrieve", 1),
Feature("user_consent", 3)
)
class SmsRetrieverService : BaseService(TAG, GmsService.SMS_RETRIEVER) {
private val smsRetriever = SmsRetrieverCore(this, lifecycle)
override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) {
val packageName = PackageUtils.getAndCheckCallingPackage(this, request.packageName)
?: throw IllegalArgumentException("Missing package name")
callback.onPostInitCompleteWithConnectionInfo(
CommonStatusCodes.SUCCESS,
SmsRetrieverServiceImpl(smsRetriever, packageName, lifecycle),
ConnectionInfo().apply { features = FEATURES }
)
}
}
class SmsRetrieverServiceImpl(private val smsRetriever: SmsRetrieverCore, private val packageName: String, override val lifecycle: Lifecycle) :
ISmsRetrieverApiService.Stub(), LifecycleOwner {
override fun startSmsRetriever(callback: ISmsRetrieverResultCallback) {
Log.d(TAG, "startSmsRetriever()")
lifecycleScope.launchWhenStarted {
val status = try {
smsRetriever.startSmsRetriever(packageName)
Status.SUCCESS
} catch (e: Exception) {
Status(CommonStatusCodes.INTERNAL_ERROR, e.message)
}
try {
callback.onResult(status)
} catch (e: Exception) {
Log.w(TAG, "Failed delivering $status for startSmsRetriever()", e)
}
}
}
override fun startWithConsentPrompt(senderPhoneNumber: String?, callback: ISmsRetrieverResultCallback) {
Log.d(TAG, "startWithConsentPrompt($senderPhoneNumber)")
lifecycleScope.launchWhenStarted {
val status = try {
smsRetriever.startWithConsentPrompt(packageName, senderPhoneNumber)
Status.SUCCESS
} catch (e: Exception) {
Status(CommonStatusCodes.INTERNAL_ERROR, e.message)
}
try {
callback.onResult(status)
} catch (e: Exception) {
Log.w(TAG, "Failed delivering $status for startWithConsentPrompt()", e)
}
}
}
override fun startSmsCodeAutofill(callback: IStatusCallback) {
Log.d(TAG, "startSmsCodeAutofill()")
try {
callback.onResult(Status(SmsRetrieverStatusCodes.API_NOT_AVAILABLE))
} catch (e: Exception) {
Log.w(TAG, "Failed delivering result for startSmsCodeAutofill()", e)
}
}
override fun checkAutofillPermissionState(callback: IAutofillPermissionStateCallback) {
Log.d(TAG, "checkAutofillPermissionState()")
try {
callback.onCheckPermissionStateResult(Status.SUCCESS, 1)
} catch (e: Exception) {
Log.w(TAG, "Failed delivering result for checkAutofillPermissionState()", e)
}
}
override fun checkOngoingSmsRequest(packageName: String?, callback: IOngoingSmsRequestCallback) {
Log.d(TAG, "checkOngoingSmsRequest($packageName)")
lifecycleScope.launchWhenStarted {
val result = try {
smsRetriever.hasOngoingUserConsentRequest()
} catch (e: Exception) {
true
}
try {
callback.onHasOngoingSmsRequestResult(Status.SUCCESS, result)
} catch (e: Exception) {
Log.w(TAG, "Failed delivering $result for checkOngoingSmsRequest()", e)
}
}
}
override fun startSmsCodeBrowser(callback: IStatusCallback) {
Log.d(TAG, "startSmsCodeBrowser()")
try {
callback.onResult(Status(SmsRetrieverStatusCodes.API_NOT_AVAILABLE))
} catch (e: Exception) {
Log.w(TAG, "Failed delivering result for startSmsCodeBrowser()", e)
}
}
}

View file

@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.auth.phone
import android.annotation.TargetApi
import android.content.Intent
import android.os.*
import android.text.Html
import android.view.Gravity
import android.view.ViewGroup.LayoutParams
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.google.android.gms.auth.api.phone.SmsRetriever
import org.microg.gms.ui.buildAlertDialog
import org.microg.gms.utils.getApplicationLabel
private const val TAG = "UserConsentPrompt"
class UserConsentPromptActivity : AppCompatActivity() {
private val messenger: Messenger?
get() = intent.getParcelableExtra(EXTRA_MESSENGER)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val callingPackage = callingActivity?.packageName ?: return finish()
val messenger = messenger ?: return finish()
messenger.send(Message.obtain().apply {
what = MSG_REQUEST_MESSAGE_BODY
replyTo = Messenger(object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
if (msg.what == MSG_REQUEST_MESSAGE_BODY) {
val message = msg.data.getString("message") ?: return
showConsentDialog(callingPackage, message)
}
}
})
})
}
@TargetApi(16)
private fun showConsentDialog(callingPackage: String, message: String) {
val view = layoutInflater.inflate(R.layout.dialog_sms_user_consent, null)
val dialog = buildAlertDialog()
.setCancelable(false)
.setView(view)
.create()
val appName = packageManager.getApplicationLabel(callingPackage)
view.findViewById<TextView>(android.R.id.title).text = Html.fromHtml(getString(R.string.sms_user_consent_title, Html.escapeHtml(appName)))
view.findViewById<TextView>(android.R.id.text1).text = message
view.findViewById<Button>(android.R.id.button2).setOnClickListener {
dialog.cancel()
}
dialog.setOnCancelListener {
setResult(RESULT_CANCELED)
finish()
}
view.findViewById<Button>(android.R.id.button1).setOnClickListener {
dialog.dismiss()
setResult(RESULT_OK, Intent().apply {
putExtra(SmsRetriever.EXTRA_SMS_MESSAGE, message)
})
messenger?.send(Message.obtain().apply {
what = MSG_CONSUME_MESSAGE
})
finish()
}
if (!dialog.isShowing) {
dialog.window?.setGravity(Gravity.BOTTOM)
dialog.window?.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
dialog.show()
}
}
}

View file

@ -0,0 +1,13 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.auth.phone
const val MSG_REQUEST_MESSAGE_BODY = 1
const val MSG_CONSUME_MESSAGE = 2
const val EXTRA_MESSENGER = "messenger"
const val EXTRA_PERMISSIONS = "permissions"
const val EXTRA_GRANT_RESULTS = "grantResults"

View file

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ SPDX-FileCopyrightText: 2023 microG Project Team
~ SPDX-License-Identifier: Apache-2.0
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:gravity="bottom|center"
android:padding="24dp"
android:orientation="vertical">
<TextView
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="textStart"
android:gravity="start"
style="@style/TextAppearance.AppCompat.Title"
tools:text="Allow App to read the message below and enter the code?" />
<TextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="textStart"
android:layout_marginTop="10dp"
android:gravity="start"
style="@style/TextAppearance.AppCompat.Small"
tools:text="OTP is 123456 to accept transaction with App. Do not share for security reasons" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_gravity="end"
android:orientation="horizontal">
<Button
android:id="@android:id/button2"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/sms_user_consent_deny"
style="@style/Widget.AppCompat.Button.Borderless" />
<Button
android:id="@android:id/button1"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:text="@string/sms_user_consent_allow"
style="@style/Widget.AppCompat.Button.Colored" />
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_deny">رفض</string>
<string name="sms_user_consent_allow">سماح</string>
<string name="sms_user_consent_title">السماح ل<b>%s</b> بقراءة الرسالة أدناه وإدخال الرمز؟</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_allow">Permitir</string>
<string name="sms_user_consent_title">¿Quies permitir que l\'aplicación <b>%s</b> llea\'l mensaxe d\'abaxo ya introduza\'l códigu\?</string>
<string name="sms_user_consent_deny">Negar</string>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title"><b>%s</b>-ə aşağıdakı mesajı oxumağa və kodu daxil etməyə icazə verilsin?</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title">Дазволіць <b>%s</b> прачытаць паведамленне і ўвесці код?</string>
<string name="sms_user_consent_allow">Дазволіць</string>
<string name="sms_user_consent_deny">Адхіліць</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_deny">Zakázat</string>
<string name="sms_user_consent_title">Povolit aplikaci <b>%s</b> zobrazit zprávu níže a zadat kód?</string>
<string name="sms_user_consent_allow">Povolit</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_allow">Erlauben</string>
<string name="sms_user_consent_deny">Ablehnen</string>
<string name="sms_user_consent_title"><b>%s</b> erlauben, die folgende Nachricht zu lesen und den Code einzugeben?</string>
</resources>

View file

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

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title">¿Permitir que <b>%s</b> lean el mensaje de abajo e introduzcan el código?</string>
<string name="sms_user_consent_allow">Permitir</string>
<string name="sms_user_consent_deny">Denegar</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_allow">پذیرفتن</string>
<string name="sms_user_consent_deny">رد کردن</string>
<string name="sms_user_consent_title">به <b>%s</b> اجازه دهید پیام زیر را بخواند و کد را وارد کنید؟</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title">Sallitaanko <b>%s</b> lukea seuraavan viestin ja syöttää koodin?</string>
<string name="sms_user_consent_allow">Salli</string>
<string name="sms_user_consent_deny">Kiellä</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title">Payagan ang <b>%s</b> na basahin ang mensahe sa ibaba at ilagay ang code?</string>
<string name="sms_user_consent_allow">Payagan</string>
<string name="sms_user_consent_deny">Tanggihan</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title">Autoriser <b>%s</b> à lire le message ci-dessous et saisir le code?</string>
<string name="sms_user_consent_allow">Autoriser</string>
<string name="sms_user_consent_deny">Refuser</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_deny">Diúltaigh</string>
<string name="sms_user_consent_title">An bhfuil cead ag <b>%s</b> an teachtaireacht thíos a léamh agus an cód a chur isteach?</string>
<string name="sms_user_consent_allow">Ceadaigh</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_allow">Izinkan</string>
<string name="sms_user_consent_title">Izinkan <b>%s</b> untuk membaca pesan di bawah ini dan memasukkan kode?</string>
<string name="sms_user_consent_deny">Tolak</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title">Leyfa <b>%s</b> að lesa skilaboðin hér fyrir neðan og setja inn kóðann?</string>
<string name="sms_user_consent_deny">Hafna</string>
<string name="sms_user_consent_allow">Leyfa</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_allow">Consenti</string>
<string name="sms_user_consent_title">Consentire a <b>%s</b> di leggere il seguente messaggio e inserire il codice\?</string>
<string name="sms_user_consent_deny">Nega</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_allow">許可</string>
<string name="sms_user_consent_title"><b>%s</b> に以下のメッセージの読み取りを許可してコードを入力させますか?</string>
<string name="sms_user_consent_deny">拒否</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title"><b>%s</b>가 아래의 메시지를 읽고 코드를 입력하도록 허용할까요?</string>
<string name="sms_user_consent_allow">허용</string>
<string name="sms_user_consent_deny">거부</string>
</resources>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_allow">Atļaut</string>
<string name="sms_user_consent_deny">Liegt</string>
</resources>

View file

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

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title">താഴെയുള്ള സന്ദേശം വായിച്ച് കോഡ് നൽകാൻ <b>%s</b> നെ അനുവദിക്കണോ?</string>
<string name="sms_user_consent_allow">അനുവദിക്കുക</string>
<string name="sms_user_consent_deny">നിരസിക്കുക</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_deny">Nekt</string>
<string name="sms_user_consent_title">La <b>%s</b> lese meldingen under og legge inn koden?</string>
<string name="sms_user_consent_allow">Tillat</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title"><b>%s</b> toestaan het onderstaande bericht te lezen en de code in te voeren?</string>
<string name="sms_user_consent_allow">Toestaan</string>
<string name="sms_user_consent_deny">Niet toestaan</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_allow">Zezwól</string>
<string name="sms_user_consent_title">Zezwolić <b>%s</b> na odczyt poniższej wiadomości i wprowadzenie kodu?</string>
<string name="sms_user_consent_deny">Odmów</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title">Permitir que <b>%s</b> leia a mensagem abaixo e insira o código?</string>
<string name="sms_user_consent_allow">Permitir</string>
<string name="sms_user_consent_deny">Negar</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title">Permitir que <b>%s</b> leia a mensagem abaixo e insira o código?</string>
<string name="sms_user_consent_allow">Permitir</string>
<string name="sms_user_consent_deny">Recusar</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_allow">Permite</string>
<string name="sms_user_consent_title">Permiți ca <b>%s</b> să citească mesajul de mai jos și să introducă codul\?</string>
<string name="sms_user_consent_deny">Refuză</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title">Разрешить <b>%s</b> прочитать сообщение ниже и ввести код?</string>
<string name="sms_user_consent_allow">Разрешить</string>
<string name="sms_user_consent_deny">Отклонить</string>
</resources>

View file

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

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_allow">Дозволи</string>
<string name="sms_user_consent_title">Дозволити <b>%s</b> да прочита поруку испод и унесе кôд\?</string>
<string name="sms_user_consent_deny">Одбиј</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_allow">Tillåt</string>
<string name="sms_user_consent_title">Vill du låta <b>%s</b> läsa meddelandet nedan och ange koden?</string>
<string name="sms_user_consent_deny">Neka</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title">கீழே உள்ள செய்தியைப் படித்து குறியீட்டை உள்ளிட <b>%s </b> ஐ அனுமதிக்கவா?</string>
<string name="sms_user_consent_allow">இசைவு</string>
<string name="sms_user_consent_deny">மறு</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title">อนุญาตให้ <b>%s</b> อ่านข้อความด้านล่างและกรอกรหัส?</string>
<string name="sms_user_consent_allow">อนุญาต</string>
<string name="sms_user_consent_deny">ปฏิเสธ</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_allow">İzin ver</string>
<string name="sms_user_consent_title"><b>%s</b> uygulamasının aşağıdaki mesajı okumasına ve kodu girmesine izin veriyor musunuz?</string>
<string name="sms_user_consent_deny">Reddet</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title"><b>%s</b> نىڭ تۆۋەندىكى مەزمۇننى ئوقۇپ ۋە كودنى كىرگۈزۈشىگە يول قويامدۇ؟</string>
<string name="sms_user_consent_deny">رەت قىل</string>
<string name="sms_user_consent_allow">يول قوي</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_allow">Дозволити</string>
<string name="sms_user_consent_title">Дозволити <b>%s</b> прочитати наведене нижче повідомлення та ввести код?</string>
<string name="sms_user_consent_deny">Відхилити</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_deny">Từ chối</string>
<string name="sms_user_consent_title">Cho phép <b>%s</b> đọc tin nhắn bên dưới và nhập mã?</string>
<string name="sms_user_consent_allow">Chấp nhận</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title">允许<b>%s</b>读取以下消息并输入代码?</string>
<string name="sms_user_consent_allow">允许</string>
<string name="sms_user_consent_deny">拒绝</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sms_user_consent_title">允許「<b>%s</b>」閱讀以下訊息並輸入代碼?</string>
<string name="sms_user_consent_deny">拒絕</string>
<string name="sms_user_consent_allow">允許</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ SPDX-FileCopyrightText: 2023 microG Project Team
~ SPDX-License-Identifier: Apache-2.0
-->
<resources>
<string name="sms_user_consent_title">Allow <b>%s</b> to read the message below and enter the code?</string>
<string name="sms_user_consent_allow">Allow</string>
<string name="sms_user_consent_deny">Deny</string>
</resources>

View file

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

View file

@ -0,0 +1,7 @@
package com.google.android.gms.auth.api.phone.internal;
import com.google.android.gms.common.api.Status;
interface IAutofillPermissionStateCallback {
void onCheckPermissionStateResult(in Status status, int result) = 0;
}

View file

@ -0,0 +1,7 @@
package com.google.android.gms.auth.api.phone.internal;
import com.google.android.gms.common.api.Status;
interface IOngoingSmsRequestCallback {
void onHasOngoingSmsRequestResult(in Status status, boolean hasOngoingSmsRequest) = 0;
}

View file

@ -0,0 +1,18 @@
package com.google.android.gms.auth.api.phone.internal;
import com.google.android.gms.auth.api.phone.internal.IAutofillPermissionStateCallback;
import com.google.android.gms.auth.api.phone.internal.IOngoingSmsRequestCallback;
import com.google.android.gms.auth.api.phone.internal.ISmsRetrieverResultCallback;
import com.google.android.gms.common.api.internal.IStatusCallback;
import com.google.android.gms.common.api.Status;
import java.lang.String;
interface ISmsRetrieverApiService {
void startSmsRetriever(ISmsRetrieverResultCallback callback) = 0;
void startWithConsentPrompt(String senderPhoneNumber, ISmsRetrieverResultCallback callback) = 1;
void startSmsCodeAutofill(IStatusCallback callback) = 2;
void checkAutofillPermissionState(IAutofillPermissionStateCallback callback) = 3;
void checkOngoingSmsRequest(String packageName, IOngoingSmsRequestCallback callback) = 4;
void startSmsCodeBrowser(IStatusCallback callback) = 5;
}

View file

@ -0,0 +1,7 @@
package com.google.android.gms.auth.api.phone.internal;
import com.google.android.gms.common.api.Status;
interface ISmsRetrieverResultCallback {
void onResult(in Status status) = 0;
}

View file

@ -0,0 +1,93 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
* Notice: Portions of this file are reproduced from work created and shared by Google and used
* according to terms described in the Creative Commons 4.0 Attribution License.
* See https://developers.google.com/readme/policies for details.
*/
package com.google.android.gms.auth.api.phone;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import android.os.Handler;
import androidx.annotation.IntDef;
import com.google.android.gms.common.api.*;
import com.google.android.gms.tasks.Task;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The interface for interacting with the SMS Code Autofill API. These methods are only supported on devices running Android P and later.
* For devices that run versions earlier than Android P, all method calls return {@link SmsRetrieverStatusCodes#PLATFORM_NOT_SUPPORTED}.
* <p>
* Note: This interface works only for the current user-designated autofill service.
* Any calls from non user-designated autofill services or other applications will fail with {@link SmsRetrieverStatusCodes#API_NOT_AVAILABLE}.
*/
public interface SmsCodeAutofillClient extends HasApiKey<Api.ApiOptions.NoOptions> {
/**
* Returns the {@link SmsCodeAutofillClient.PermissionState} of the current user-designated autofill service.
* The result could be {@code NONE}, {@code GRANTED}, or {@code DENIED}.
* <p>
* The autofill service should check its permission state prior to showing the suggestion prompt for retrieving an SMS
* verification code, because it will definitely fail on calling {@link #startSmsCodeRetriever()} in permission denied state.
*/
Task<@PermissionState Integer> checkPermissionState();
/**
* Returns {@code true} if there are requests from {@link SmsRetriever} in progress for the given package name.
* <p>
* The autofill service can check this method to avoid showing a suggestion prompt for retrieving an SMS verification code,
* in case that a user app may already be retrieving the SMS verification code through {@link SmsRetriever}.
* <p>
* Note: This result does not include those requests from {@code SmsCodeAutofillClient}.
*/
Task<Boolean> hasOngoingSmsRequest(String packageName);
/**
* Starts {@code SmsCodeRetriever}, which looks for an SMS verification code from messages recently received (up to 1 minute
* prior). If there is no SMS verification code found from the SMS inbox, it waits for new incoming SMS messages until it
* finds an SMS verification code or reaches the timeout (about 5 minutes).
* <p>
* The SMS verification code will be sent via a Broadcast Intent with {@link SmsCodeRetriever#SMS_CODE_RETRIEVED_ACTION}. This Intent contains
* Extras with keys {@link SmsCodeRetriever#EXTRA_SMS_CODE} for the retrieved verification code as a {@code String}, and {@link SmsCodeRetriever#EXTRA_STATUS} for {@link Status} to
* indicate {@code RESULT_SUCCESS}, {@code RESULT_TIMEOUT} or {@link SmsRetrieverStatusCodes}.
* <p>
* Note: Add {@link SmsRetriever#SEND_PERMISSION} in {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)} while
* registering the receiver to detect that the broadcast intent is from the SMS Retriever.
*/
Task<Void> startSmsCodeRetriever();
/**
* Permission states for the current user-designated autofill service. The initial state is {@code NONE} upon the first time using the
* SMS Code Autofill API. This permission can be granted or denied through a consent dialog requested by the current
* autofill service, or an explicit change by users within the SMS verification codes settings.
*/
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.SOURCE)
@IntDef({PermissionState.NONE, PermissionState.GRANTED, PermissionState.DENIED})
@interface PermissionState {
/**
* Indicates that the current autofill service has not been granted or denied permission by the user. Calling
* {@link #startSmsCodeRetriever()} will fail with {@link CommonStatusCodes#RESOLUTION_REQUIRED}. The caller can use
* {@link ResolvableApiException#startResolutionForResult(Activity, int)} to show a consent dialog for requesting permission from the user.
*/
int NONE = 0;
/**
* Indicates that the current autofill service has been granted permission by the user. The user consent is not required for
* calling {@link #startSmsCodeRetriever()} in this state.
*/
int GRANTED = 1;
/**
* Indicates that the current autofill service has been denied permission by the user. Calling {@link #startSmsCodeRetriever()}
* will fail with {@link SmsRetrieverStatusCodes#USER_PERMISSION_REQUIRED}. It can only be resolved by the user explicitly turning on the permission
* in settings.
*/
int DENIED = 2;
}
}

View file

@ -0,0 +1,58 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
* Notice: Portions of this file are reproduced from work created and shared by Google and used
* according to terms described in the Creative Commons 4.0 Attribution License.
* See https://developers.google.com/readme/policies for details.
*/
package com.google.android.gms.auth.api.phone;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.HasApiKey;
import com.google.android.gms.common.api.ResolvableApiException;
import com.google.android.gms.tasks.Task;
/**
* The interface for interacting with the SMS Code Browser API. By using {@link #startSmsCodeRetriever()}, you can retrieve the
* origin-bound one-time code from SMS messages.
* <p>
* The SMS message format should follow the origin-bound one-time code specification:
* <ul>
* <li>Can optionally begin with human-readable explanatory text. This consists of all but the last line of the message.</li>
* <li>The last line of the message contains both a host and a code, each prefixed with a sigil: U+0040 (@) before the host, and U+0023 (#) before the code.</li>
* </ul>
* <p>
* Note: This interface works only for the default browser app set by the current user. Any other calls will fail with {@link SmsRetrieverStatusCodes#API_NOT_AVAILABLE}.
*/
public interface SmsCodeBrowserClient extends HasApiKey<Api.ApiOptions.NoOptions> {
/**
* Starts {@code SmsCodeRetriever}, which looks for an origin-bound one-time code from SMS messages recently received (up to
* 1 minute prior). If there is no matching message found from the SMS inbox, it waits for new incoming SMS messages
* until it finds a matching message or reaches the timeout (about 5 minutes). Calling this method multiple times only
* returns one result, but it can extend the timeout period to the last call. Once the result is returned or it reaches
* the timeout, SmsCodeRetriever will stop automatically.
* <p>
* The SMS verification code will be sent via a Broadcast Intent with {@link SmsCodeRetriever#SMS_CODE_RETRIEVED_ACTION}.
* This Intent contains Extras with keys:
* <ul>
* <li>{@link SmsCodeRetriever#EXTRA_SMS_CODE_LINE} for the retrieved line that contains the origin-bound one-time code and the metadata, or
* {@code null} in failed cases.</li>
* <li>{@link SmsCodeRetriever#EXTRA_STATUS} for the Status to indicate {@code RESULT_SUCCESS}, {@code RESULT_TIMEOUT} or other {@link SmsRetrieverStatusCodes}.</li>
* </ul>
* If the caller has not been granted or denied permission by the user, it will fail with a {@link ResolvableApiException}. The
* caller can use {@link ResolvableApiException#startResolutionForResult(Activity, int)} to show a consent dialog for requesting permission from
* the user. The dialog result is returned via {@link Activity#onActivityResult(int, int, Intent)}. If the user grants the permission,
* the activity result returns with {@code RESULT_OK}. Then you can start the retriever again to retrieve the verification code.
* <p>
* Note: Add {@link SmsRetriever#SEND_PERMISSION} in {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)} while
* registering the receiver to detect that the broadcast intent is from the SMS Retriever.
*/
Task<Void> startSmsCodeRetriever();
}

View file

@ -0,0 +1,89 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
* Notice: Portions of this file are reproduced from work created and shared by Google and used
* according to terms described in the Creative Commons 4.0 Attribution License.
* See https://developers.google.com/readme/policies for details.
*/
package com.google.android.gms.auth.api.phone;
import android.app.Activity;
import android.content.Context;
import androidx.annotation.NonNull;
import com.google.android.gms.common.api.Status;
import org.microg.gms.auth.api.phone.SmsCodeAutofillClientImpl;
import org.microg.gms.auth.api.phone.SmsCodeBrowserClientImpl;
/**
* {@code SmsCodeRetriever} is a variant of {@link SmsRetriever}, and it provides access to Google services that help you retrieve SMS
* verification codes sent to the user's device, without having to ask for {@code android.permission.READ_SMS} or {@code android.permission.RECEIVE_SMS}.
* <p>
* To use {@code SmsCodeRetriever} in the Android autofill service, obtain an instance of {@link SmsCodeAutofillClient} using
* {@link #getAutofillClient(Context)} or {@link #getAutofillClient(Activity)}, and start SMS Code Retriever service by calling
* {@link SmsCodeAutofillClient#startSmsCodeRetriever()}. To use it in the browser app, you obtain an instance of {@link SmsCodeBrowserClient} using
* {@link #getBrowserClient(Context)} or {@link #getBrowserClient(Activity)} instead.
* <p>
* The service first looks for an SMS verification code from messages recently received (up to 1 minute prior). If there is no
* SMS verification code found from the SMS inbox, it waits for new incoming SMS messages until it finds an SMS
* verification code or reaches the timeout (about 5 minutes).
*/
public class SmsCodeRetriever {
/**
* Intent extra key of the retrieved SMS verification code by the {@link SmsCodeAutofillClient}.
*/
@NonNull
public static final String EXTRA_SMS_CODE = "com.google.android.gms.auth.api.phone.EXTRA_SMS_CODE";
/**
* Intent extra key of the retrieved SMS verification code line by the {@link SmsCodeBrowserClient}.
*/
@NonNull
public static final String EXTRA_SMS_CODE_LINE = "com.google.android.gms.auth.api.phone.EXTRA_SMS_CODE_LINE";
/**
* Intent extra key of {@link Status}, which indicates {@code RESULT_SUCCESS}, {@code RESULT_TIMEOUT} or {@link SmsRetrieverStatusCodes}.
*/
@NonNull
public static final String EXTRA_STATUS = "com.google.android.gms.auth.api.phone.EXTRA_STATUS";
/**
* Intent action when an SMS verification code is retrieved.
*/
@NonNull
public static final String SMS_CODE_RETRIEVED_ACTION = "com.google.android.gms.auth.api.phone.SMS_CODE_RETRIEVED";
/**
* Creates a new instance of {@link SmsCodeAutofillClient} for use in an {@link Activity}.
* This {@link SmsCodeAutofillClient} is intended to be used by the current user-designated autofill service only.
*/
@NonNull
public static SmsCodeAutofillClient getAutofillClient(Activity activity) {
return new SmsCodeAutofillClientImpl(activity);
}
/**
* Creates a new instance of {@link SmsCodeAutofillClient} for use in a {@link Context}.
* This {@link SmsCodeAutofillClient} is intended to be used by the current user-designated autofill service only.
*/
@NonNull
public static SmsCodeAutofillClient getAutofillClient(Context context) {
return new SmsCodeAutofillClientImpl(context);
}
/**
* Creates a new instance of {@link SmsCodeBrowserClient} for use in an {@link Activity}.
* This {@link SmsCodeBrowserClient} is intended to be used by the default browser app only.
*/
@NonNull
public static SmsCodeBrowserClient getBrowserClient(Activity activity) {
return new SmsCodeBrowserClientImpl(activity);
}
/**
* Creates a new instance of {@link SmsCodeBrowserClient} for use in a {@link Context}.
* This {@link SmsCodeBrowserClient} is intended to be used by the default browser app only.
*/
@NonNull
public static SmsCodeBrowserClient getBrowserClient(Context context) {
return new SmsCodeBrowserClientImpl(context);
}
}

View file

@ -0,0 +1,72 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
* Notice: Portions of this file are reproduced from work created and shared by Google and used
* according to terms described in the Creative Commons 4.0 Attribution License.
* See https://developers.google.com/readme/policies for details.
*/
package com.google.android.gms.auth.api.phone;
import android.app.Activity;
import android.content.Context;
import androidx.annotation.NonNull;
import com.google.android.gms.common.api.Status;
import org.microg.gms.auth.api.phone.SmsRetrieverClientImpl;
/**
* {@code SmsRetriever} provides access to Google services that help you retrieve SMS messages sent to your app without
* having to ask for {@code android.permission.READ_SMS} or {@code android.permission.RECEIVE_SMS}.
* <p>
* To use {@code SmsRetriever}, obtain an instance of {@link SmsRetrieverClient} using {@link #getClient(Context)} or
* {@link #getClient(Activity)}, then start the SMS retriever service by calling {@link SmsRetrieverClient#startSmsRetriever()} or
* {@link SmsRetrieverClient#startSmsUserConsent(String)}. The service waits for a matching SMS message until timeout (5 minutes).
*/
public class SmsRetriever {
/**
* Intent extra key of the consent intent to be launched from client app.
*/
@NonNull
public static final String EXTRA_CONSENT_INTENT = "com.google.android.gms.auth.api.phone.EXTRA_CONSENT_INTENT";
/**
* [Optional] Intent extra key of the retrieved Sim card subscription Id if any, as an {@code int}.
*/
@NonNull
public static final String EXTRA_SIM_SUBSCRIPTION_ID = "com.google.android.gms.auth.api.phone.EXTRA_SIM_SUBSCRIPTION_ID";
/**
* Intent extra key of the retrieved SMS message as a {@code String}.
*/
@NonNull
public static final String EXTRA_SMS_MESSAGE = "com.google.android.gms.auth.api.phone.EXTRA_SMS_MESSAGE";
/**
* Intent extra key of {@link Status}, which indicates SUCCESS or TIMEOUT.
*/
@NonNull
public static final String EXTRA_STATUS = "com.google.android.gms.auth.api.phone.EXTRA_STATUS";
/**
* Permission that's used to register the receiver to detect that the broadcaster is the SMS Retriever.
*/
@NonNull
public static final String SEND_PERMISSION = "com.google.android.gms.auth.api.phone.permission.SEND";
/**
* Intent action when SMS message is retrieved.
*/
@NonNull
public static final String SMS_RETRIEVED_ACTION = "com.google.android.gms.auth.api.phone.SMS_RETRIEVED";
/**
* Create a new instance of {@link SmsRetrieverClient} for use in an {@link Activity}.
*/
@NonNull
public static SmsRetrieverClient getClient(Activity activity) {
return new SmsRetrieverClientImpl(activity);
}
/**
* Create a new instance of {@link SmsRetrieverClient} for use in a {@link Context}.
*/
@NonNull
public static SmsRetrieverClient getClient(Context context) {
return new SmsRetrieverClientImpl(context);
}
}

View file

@ -0,0 +1,58 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
* Notice: Portions of this file are reproduced from work created and shared by Google and used
* according to terms described in the Creative Commons 4.0 Attribution License.
* See https://developers.google.com/readme/policies for details.
*/
package com.google.android.gms.auth.api.phone;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
/**
* API interface for SmsRetriever.
*/
public interface SmsRetrieverApi {
/**
* Starts {@code SmsRetriever}, which waits for a matching SMS message until timeout (5 minutes). The matching SMS message
* will be sent via a Broadcast Intent with action {@link SmsRetriever#SMS_RETRIEVED_ACTION}. The Intent contains Extras with keys
* {@link SmsRetriever#EXTRA_SMS_MESSAGE} for the retrieved SMS message as a String, and {@link SmsRetriever#EXTRA_STATUS} for {@link Status} to indicate
* {@code SUCCESS}, {@code DEVELOPER_ERROR}, {@code ERROR}, or {@code TIMEOUT}.
* <p>
* Note: Add {@link SmsRetriever#SEND_PERMISSION} while registering the receiver to detect that the broadcast intent is from the SMS Retriever.
* <p>
* The possible causes for errors are:
* <ul>
* <li>DEVELOPER_ERROR: the caller app has incorrect number of certificates. Only one certificate is allowed.</li>
* <li>ERROR: the AppCode collides with other installed apps.</li>
* </ul>
*
* @return a Task for the call. Attach an {@link OnCompleteListener} and then check {@link Task#isSuccessful()} to determine if it was successful.
*/
@NonNull
Task<Void> startSmsRetriever();
/**
* Starts {@code SmsUserConsent}, which waits for an OTP-containing SMS message until timeout (5 minutes). OTP-containing
* SMS message can be retrieved with two steps.
* <p>
* Note: Add {@link SmsRetriever#SEND_PERMISSION} while registering the receiver to detect that the broadcast intent is from the SMS Retriever.
* <ol>
* <li>[Get consent Intent] While OTP-containing SMS message comes, a consent Intent will be sent via a Broadcast
* Intent with action {@link SmsRetriever#SMS_RETRIEVED_ACTION}. The Intent contains Extras with keys {@link SmsRetriever#EXTRA_CONSENT_INTENT} for the
* consent Intent and {@link SmsRetriever#EXTRA_STATUS} for {@link Status} to indicate {@code SUCCESS} or {@code TIMEOUT}.</li>
* <li>[Get OTP-containing SMS message] Calls {@code startActivityForResult} with consent Intent to launch a consent
* dialog to get user's approval, then the OTP-containing SMS message can be retrieved from the activity result.</li>
* </ol>
*
* @param senderAddress address of desired SMS sender, or {@code null} to retrieve any sender
* @return a Task for the call. Attach an {@link OnCompleteListener} and then check {@link Task#isSuccessful()} to determine if it was successful.
*/
@NonNull
Task<Void> startSmsUserConsent(@Nullable String senderAddress);
}

View file

@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
* Notice: Portions of this file are reproduced from work created and shared by Google and used
* according to terms described in the Creative Commons 4.0 Attribution License.
* See https://developers.google.com/readme/policies for details.
*/
package com.google.android.gms.auth.api.phone;
import android.content.Context;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.GoogleApi;
import com.google.android.gms.common.api.GoogleApiClient;
import org.microg.gms.auth.api.phone.SmsRetrieverApiClient;
/**
* The main entry point for interacting with SmsRetriever.
* <p>
* This does not require a {@link GoogleApiClient}. See {@link GoogleApi} for more information.
*/
public abstract class SmsRetrieverClient extends GoogleApi<Api.ApiOptions.NoOptions> implements SmsRetrieverApi {
protected SmsRetrieverClient(Context context) {
super(context, SmsRetrieverApiClient.API, Api.ApiOptions.NO_OPTIONS);
}
}

View file

@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
* Notice: Portions of this file are reproduced from work created and shared by Google and used
* according to terms described in the Creative Commons 4.0 Attribution License.
* See https://developers.google.com/readme/policies for details.
*/
package com.google.android.gms.auth.api.phone;
import androidx.annotation.NonNull;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Status;
/**
* SMS Retriever specific status codes, for use in {@link Status#getStatusCode()}.
*/
public class SmsRetrieverStatusCodes extends CommonStatusCodes {
/**
* The current Android platform does not support this particular API.
*/
public static final int PLATFORM_NOT_SUPPORTED = 36500;
/**
* The calling application is not eligible to use this particular API.
* <p>
* Note: For {@link SmsCodeAutofillClient}, this status indicates that the calling application is not the current user-designated
* autofill service. For {@link SmsCodeBrowserClient}, it indicates that the caller is not the system default browser app.
*/
public static final int API_NOT_AVAILABLE = 36501;
/**
* The user has not granted the calling application permission to use this particular API.
*/
public static final int USER_PERMISSION_REQUIRED = 36502;
/**
* Returns an untranslated debug string based on the given status code.
*/
@NonNull
public static String getStatusCodeString(int statusCode) {
switch (statusCode) {
case PLATFORM_NOT_SUPPORTED:
return "PLATFORM_NOT_SUPPORTED";
case API_NOT_AVAILABLE:
return "API_NOT_AVAILABLE";
case USER_PERMISSION_REQUIRED:
return "USER_PERMISSION_REQUIRED";
default:
return CommonStatusCodes.getStatusCodeString(statusCode);
}
}
}

View file

@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: CC-BY-4.0
* Notice: Portions of this file are reproduced from work created and shared by Google and used
* according to terms described in the Creative Commons 4.0 Attribution License.
* See https://developers.google.com/readme/policies for details.
*/
/**
* {@code SmsRetriever} contains two APIs, the SMS Retriever API and the SMS User Consent API, that provide access to Google
* services that help you retrieve SMS messages directed to your app, without having to ask for
* {@code android.permission.READ_SMS} or {@code android.permission.RECEIVE_SMS}. The {@code SmsCodeRetriever} is for autofill
* services and browser apps to retrieve SMS-based verification codes.
* <p>
* Many apps use phone numbers to verify the identity of a user. The app sends an SMS message containing an OTP (One
* Time Passcode) to the user, who then enters the OTP from the received SMS message to prove ownership of the phone number.
* <p>
* In Android, to provide a streamlined UX, an app may request the SMS read permission, and retrieve the OTP
* automatically. This is problematic since this permission allows the app to read other SMS messages which may contain
* the user's private information. Also, the latest Play Store policy changes restrict access to SMS messages.
* <p>
* The SMS Retriever API solves this problem by providing app developers a way to automatically retrieve only the SMS
* directed to the app without asking for the SMS read permission or gaining the ability to read any other SMS messages on the device.
* <p>
* The SMS User Consent API complements the SMS Retriever API by allowing an app to prompt the user to grant access to
* the content of the next SMS message that contains an OTP. When a user gives consent, the app will then have access to
* the entire message body to automatically complete SMS verification.
* <p>
* The SMS Retriever API completely automates the SMS-based OTP verification process for the user. However, there are
* situations where you dont control the format of the SMS message and as a result cannot use the SMS Retriever API.
* In these situations, you can use the SMS User Consent API to streamline the process.
* <p>
* With the SMS Code Autofill API, a user-designated autofill service can retrieve the SMS verification codes from the SMS
* inbox or new incoming SMS messages, then fill in this code for a user to complete any SMS verification requests in a
* user app. For browser apps, you can achieve this by using the SMS Code Browser API.
*/
package com.google.android.gms.auth.api.phone;

View file

@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.auth.api.phone;
import android.content.Context;
import com.google.android.gms.auth.api.phone.SmsCodeAutofillClient;
import com.google.android.gms.auth.api.phone.internal.IAutofillPermissionStateCallback;
import com.google.android.gms.auth.api.phone.internal.IOngoingSmsRequestCallback;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.GoogleApi;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.tasks.Task;
import org.microg.gms.common.api.PendingGoogleApiCall;
public class SmsCodeAutofillClientImpl extends GoogleApi<Api.ApiOptions.NoOptions> implements SmsCodeAutofillClient {
public SmsCodeAutofillClientImpl(Context context) {
super(context, SmsRetrieverApiClient.API, Api.ApiOptions.NO_OPTIONS);
}
@Override
public Task<@PermissionState Integer> checkPermissionState() {
return scheduleTask((PendingGoogleApiCall<Integer, SmsRetrieverApiClient>) (client, completionSource) -> client.checkAutofillPermissionState(new IAutofillPermissionStateCallback.Stub() {
@Override
public void onCheckPermissionStateResult(Status status, int result) {
if (status.isSuccess()) {
completionSource.trySetResult(result);
} else {
completionSource.trySetException(new ApiException(status));
}
}
}));
}
@Override
public Task<Boolean> hasOngoingSmsRequest(String packageName) {
return scheduleTask((PendingGoogleApiCall<Boolean, SmsRetrieverApiClient>) (client, completionSource) -> client.checkOngoingSmsRequest(packageName, new IOngoingSmsRequestCallback.Stub() {
@Override
public void onHasOngoingSmsRequestResult(Status status, boolean result) {
if (status.isSuccess()) {
completionSource.trySetResult(result);
} else {
completionSource.trySetException(new ApiException(status));
}
}
}));
}
@Override
public Task<Void> startSmsCodeRetriever() {
return scheduleTask((PendingGoogleApiCall<Void, SmsRetrieverApiClient>) (client, completionSource) -> client.startSmsCodeAutofill(new StatusCallbackImpl(completionSource)));
}
}

View file

@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.auth.api.phone;
import android.content.Context;
import com.google.android.gms.auth.api.phone.SmsCodeBrowserClient;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.GoogleApi;
import com.google.android.gms.tasks.Task;
import org.microg.gms.common.api.PendingGoogleApiCall;
public class SmsCodeBrowserClientImpl extends GoogleApi<Api.ApiOptions.NoOptions> implements SmsCodeBrowserClient {
public SmsCodeBrowserClientImpl(Context context) {
super(context, SmsRetrieverApiClient.API, Api.ApiOptions.NO_OPTIONS);
}
@Override
public Task<Void> startSmsCodeRetriever() {
return scheduleTask((PendingGoogleApiCall<Void, SmsRetrieverApiClient>) (client, completionSource) -> client.startSmsCodeBrowser(new StatusCallbackImpl(completionSource)));
}
}

View file

@ -0,0 +1,102 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.auth.api.phone;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import androidx.annotation.Nullable;
import com.google.android.gms.auth.api.phone.internal.IAutofillPermissionStateCallback;
import com.google.android.gms.auth.api.phone.internal.IOngoingSmsRequestCallback;
import com.google.android.gms.auth.api.phone.internal.ISmsRetrieverApiService;
import com.google.android.gms.auth.api.phone.internal.ISmsRetrieverResultCallback;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.common.api.internal.IStatusCallback;
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 SmsRetrieverApiClient extends GmsClient<ISmsRetrieverApiService> {
public static final Api<Api.ApiOptions.NoOptions> API = new Api<>((options, context, looper, clientSettings, callbacks, connectionFailedListener) -> new SmsRetrieverApiClient(context, callbacks, connectionFailedListener));
public SmsRetrieverApiClient(Context context, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener) {
super(context, callbacks, connectionFailedListener, GmsService.SMS_RETRIEVER.ACTION);
serviceId = GmsService.SMS_RETRIEVER.SERVICE_ID;
}
@Override
protected ISmsRetrieverApiService interfaceFromBinder(IBinder binder) {
return ISmsRetrieverApiService.Stub.asInterface(binder);
}
public void startSmsRetriever(ISmsRetrieverResultCallback callback) {
try {
getServiceInterface().startSmsRetriever(callback);
} catch (RemoteException e) {
try {
callback.onResult(Status.INTERNAL_ERROR);
} catch (RemoteException ignored) {
}
}
}
public void startWithConsentPrompt(@Nullable String senderAddress, ISmsRetrieverResultCallback callback) {
try {
getServiceInterface().startWithConsentPrompt(senderAddress, callback);
} catch (RemoteException e) {
try {
callback.onResult(Status.INTERNAL_ERROR);
} catch (RemoteException ignored) {
}
}
}
public void startSmsCodeAutofill(IStatusCallback callback) {
try {
getServiceInterface().startSmsCodeAutofill(callback);
} catch (RemoteException e) {
try {
callback.onResult(Status.INTERNAL_ERROR);
} catch (RemoteException ignored) {
}
}
}
public void checkAutofillPermissionState(IAutofillPermissionStateCallback callback) {
try {
getServiceInterface().checkAutofillPermissionState(callback);
} catch (RemoteException e) {
try {
callback.onCheckPermissionStateResult(Status.INTERNAL_ERROR, -1);
} catch (RemoteException ignored) {
}
}
}
public void checkOngoingSmsRequest(String packageName, IOngoingSmsRequestCallback callback) {
try {
getServiceInterface().checkOngoingSmsRequest(packageName, callback);
} catch (RemoteException e) {
try {
callback.onHasOngoingSmsRequestResult(Status.INTERNAL_ERROR, false);
} catch (RemoteException ignored) {
}
}
}
public void startSmsCodeBrowser(IStatusCallback callback) {
try {
getServiceInterface().startSmsCodeBrowser(callback);
} catch (RemoteException e) {
try {
callback.onResult(Status.INTERNAL_ERROR);
} catch (RemoteException ignored) {
}
}
}
}

View file

@ -0,0 +1,32 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.auth.api.phone;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.gms.auth.api.phone.SmsRetrieverClient;
import com.google.android.gms.tasks.Task;
import org.microg.gms.common.api.PendingGoogleApiCall;
public class SmsRetrieverClientImpl extends SmsRetrieverClient {
public SmsRetrieverClientImpl(Context context) {
super(context);
}
@NonNull
@Override
public Task<Void> startSmsRetriever() {
return scheduleTask((PendingGoogleApiCall<Void, SmsRetrieverApiClient>) (client, completionSource) -> client.startSmsRetriever(new SmsRetrieverResultCallbackImpl(completionSource)));
}
@NonNull
@Override
public Task<Void> startSmsUserConsent(@Nullable String senderAddress) {
return scheduleTask((PendingGoogleApiCall<Void, SmsRetrieverApiClient>) (client, completionSource) -> client.startWithConsentPrompt(senderAddress, new SmsRetrieverResultCallbackImpl(completionSource)));
}
}

View file

@ -0,0 +1,28 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.auth.api.phone;
import com.google.android.gms.auth.api.phone.internal.ISmsRetrieverResultCallback;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.tasks.TaskCompletionSource;
class SmsRetrieverResultCallbackImpl extends ISmsRetrieverResultCallback.Stub {
private final TaskCompletionSource<Void> completionSource;
public SmsRetrieverResultCallbackImpl(TaskCompletionSource<Void> completionSource) {
this.completionSource = completionSource;
}
@Override
public void onResult(Status status) {
if (status.isSuccess()) {
completionSource.trySetResult(null);
} else {
completionSource.trySetException(new ApiException(status));
}
}
}

View file

@ -0,0 +1,28 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.auth.api.phone;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.common.api.internal.IStatusCallback;
import com.google.android.gms.tasks.TaskCompletionSource;
class StatusCallbackImpl extends IStatusCallback.Stub {
private final TaskCompletionSource<Void> completionSource;
public StatusCallbackImpl(TaskCompletionSource<Void> completionSource) {
this.completionSource = completionSource;
}
@Override
public void onResult(Status status) {
if (status.isSuccess()) {
completionSource.trySetResult(null);
} else {
completionSource.trySetException(new ApiException(status));
}
}
}