Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c316ead4 authored by Shuang Hao's avatar Shuang Hao Committed by Android (Google) Code Review
Browse files

Merge "Refactor basing on design input: 1. Merge RequestRepository and...

Merge "Refactor basing on design input: 1. Merge RequestRepository and PasswordRepository into CredentialManagerClient. 2. Split CredentialManagerClient into interface and implementation. 3. miscellaneous renaming." into main
parents d0341aa5 82105523
Loading
Loading
Loading
Loading
+30 −19
Original line number Diff line number Diff line
@@ -19,35 +19,46 @@ package com.android.credentialmanager
import android.content.Intent
import android.content.pm.PackageManager
import android.credentials.ui.RequestInfo
import android.util.Log
import com.android.credentialmanager.ktx.appLabel
import com.android.credentialmanager.ktx.cancelUiRequest
import com.android.credentialmanager.ktx.requestInfo
import com.android.credentialmanager.mapper.toGet
import com.android.credentialmanager.mapper.toRequestCancel
import com.android.credentialmanager.mapper.toRequestClose
import com.android.credentialmanager.model.Request

fun Intent.parse(
    packageManager: PackageManager,
    previousIntent: Intent? = null,
): Request {
    this.toRequestClose(previousIntent)?.let { closeRequest ->
        return closeRequest
    return parseCancelUiRequest(packageManager)
        ?: parseRequestInfo()
}

    this.toRequestCancel(packageManager)?.let { cancelRequest ->
        return cancelRequest
fun Intent.parseCancelUiRequest(packageManager: PackageManager): Request? =
    this.cancelUiRequest?.let { cancelUiRequest ->
        val showCancel = cancelUiRequest.shouldShowCancellationUi().apply {
            Log.d(TAG, "Received UI cancel request, shouldShowCancellationUi: $this")
        }

    return when (requestInfo?.type) {
        RequestInfo.TYPE_CREATE -> {
            Request.Create
        if (showCancel) {
            val appLabel = packageManager.appLabel(cancelUiRequest.appPackageName)
            if (appLabel == null) {
                Log.d(TAG, "Received UI cancel request with an invalid package name.")
                null
            } else {
                Request.Cancel(appName = appLabel, token = cancelUiRequest.token)
            }
        } else {
            Request.Close(cancelUiRequest.token)
        }

        RequestInfo.TYPE_GET -> {
            this.toGet()
    }

fun Intent.parseRequestInfo(): Request =
    requestInfo.let{ info ->
        when (info?.type) {
            RequestInfo.TYPE_CREATE -> Request.Create(info.token)
            RequestInfo.TYPE_GET -> toGet()
            else -> {
            throw IllegalStateException("Unrecognized request type: ${requestInfo?.type}")
                throw IllegalStateException("Unrecognized request type: ${info?.type}")
            }
        }
    }
+57 −0
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@
 * 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.0N
 *      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,
@@ -14,44 +14,44 @@
 * limitations under the License.
 */

package com.android.credentialmanager.repository
package com.android.credentialmanager.client

import android.content.Intent
import android.credentials.ui.BaseDialogResult
import android.credentials.ui.ProviderPendingIntentResponse
import android.credentials.ui.UserSelectionDialogResult
import android.os.Bundle
import android.util.Log
import com.android.credentialmanager.TAG
import com.android.credentialmanager.model.Password
import com.android.credentialmanager.model.Request
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.flow.StateFlow

@Singleton
class PasswordRepository @Inject constructor() {
interface CredentialManagerClient {
    /** The UI should monitor the request update. */
    val requests: StateFlow<Request?>

    suspend fun selectPassword(
        password: Password,
        request: Request.Get,
        resultCode: Int? = null,
        resultData: Intent? = null,
    ) {
        Log.d(TAG, "password selected: {provider=${password.providerId}" +
            ", key=${password.entry.key}, subkey=${password.entry.subkey}}")
    /** The UI got a new intent; update the request state. */
    fun updateRequest(intent: Intent)

        val userSelectionDialogResult = UserSelectionDialogResult(
            request.token,
            password.providerId,
            password.entry.key,
            password.entry.subkey,
            if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
    /** Sends an error encountered during the UI. */
    fun sendError(
        @BaseDialogResult.ResultCode resultCode: Int,
        errorMessage: String? = null,
    )
        val resultDataBundle = Bundle()
        UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultDataBundle)
        request.resultReceiver?.send(
            BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION,
            resultDataBundle
        )
    }

    /**
     * Sends a response to the system service. The response
     * contains information about the user's choice from the selector
     * UI and the result of the provider operation launched with
     * that selection.
     *
     * If the user choice was a normal entry, then the UI can finish
     * the activity immediately. Otherwise if it was an authentication
     * (locked) entry, then the UI will need to stay up and wait for
     * a new intent from the system containing the new data for
     * display.
     *
     * Note that if the provider operation returns RESULT_CANCELED,
     * then the selector should not send that result back, and instead
     * re-display the options to allow a user to have another choice.
     *
     * @throws [IllegalStateException] if [requests] is not [Request.Get].
     */
    fun sendResult(result: UserSelectionDialogResult)
}
 No newline at end of file
+71 −0
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@
 * 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.0N
 *      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,
@@ -14,35 +14,58 @@
 * limitations under the License.
 */

package com.android.credentialmanager.repository
package com.android.credentialmanager.client.impl

import android.content.Intent
import android.content.pm.PackageManager
import android.credentials.ui.BaseDialogResult
import android.credentials.ui.UserSelectionDialogResult
import android.os.Bundle
import android.util.Log
import com.android.credentialmanager.TAG
import com.android.credentialmanager.model.Request
import com.android.credentialmanager.parse
import com.android.credentialmanager.client.CredentialManagerClient
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class RequestRepository @Inject constructor(
class CredentialManagerClientImpl @Inject constructor(
        private val packageManager: PackageManager,
) {
) : CredentialManagerClient {

    private val _requests = MutableStateFlow<Request?>(null)
    val requests: StateFlow<Request?> = _requests
    override val requests: StateFlow<Request?> = _requests

    suspend fun processRequest(intent: Intent, previousIntent: Intent? = null) {

    override fun updateRequest(intent: Intent) {
        val request = intent.parse(
            packageManager = packageManager,
            previousIntent = previousIntent
        )
        Log.d(TAG, "Request parsed: $request, client instance: $this")
        if (request is Request.Cancel || request is Request.Close) {
            if (request.token != null && request.token != _requests.value?.token) {
                Log.w(TAG, "drop terminate request for previous session.")
                return
            }
        }
        _requests.value = request
    }

        Log.d(TAG, "Request parsed: $request")
    override fun sendError(resultCode: Int, errorMessage: String?) {
        TODO("b/300422310 - [Wear] Implement UI for cancellation request with message")
    }

        _requests.value = request
    override fun sendResult(result: UserSelectionDialogResult) {
        val currentRequest = requests.value
        check(currentRequest is Request.Get) { "current request is not get." }
        currentRequest.resultReceiver?.let { receiver ->
            val resultDataBundle = Bundle()
            UserSelectionDialogResult.addToBundle(result, resultDataBundle)
            receiver.send(
                BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION,
                resultDataBundle
            )
        }
    }
}
+0 −36
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.0N
 *
 * 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.
 */

package com.android.credentialmanager.mapper

import android.content.Intent
import android.content.pm.PackageManager
import android.util.Log
import com.android.credentialmanager.TAG
import com.android.credentialmanager.ktx.appLabel
import com.android.credentialmanager.ktx.cancelUiRequest
import com.android.credentialmanager.model.Request

fun Intent.toRequestCancel(packageManager: PackageManager): Request.Cancel? =
    this.cancelUiRequest?.let { cancelUiRequest ->
        val appLabel = packageManager.appLabel(cancelUiRequest.appPackageName)
        if (appLabel == null) {
            Log.d(TAG, "Received UI cancel request with an invalid package name.")
            null
        } else {
            Request.Cancel(appName = appLabel)
        }
    }
+0 −49
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.0N
 *
 * 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.
 */

package com.android.credentialmanager.mapper

import android.content.Intent
import com.android.credentialmanager.ktx.cancelUiRequest
import com.android.credentialmanager.ktx.requestInfo
import com.android.credentialmanager.model.Request

fun Intent.toRequestClose(
    previousIntent: Intent? = null,
): Request.Close? {
    // Close request comes as "Cancel" request from Credential Manager API
    this.cancelUiRequest?.let { cancelUiRequest ->

        if (cancelUiRequest.shouldShowCancellationUi()) {
            // Current request is to Cancel and not to Close
            return null
        }

        previousIntent?.let {
            val previousToken = previousIntent.requestInfo?.token
            val currentToken = this.requestInfo?.token

            if (previousToken != currentToken) {
                // Current cancellation is for a different request, don't close the current flow.
                return null
            }
        }

        return Request.Close
    }

    return null
}
 No newline at end of file
Loading