আপনার ক্রেডেনশিয়াল প্রদানকারী সমাধানের সাথে ক্রেডেনশিয়াল ম্যানেজার ইন্টিগ্রেট করুন

ক্রেডেনশিয়াল ম্যানেজার বলতে Android 14-এ চালু করা API-এর সেটকে বোঝায় যা ইউজারনেম-পাসওয়ার্ড, পাসকী ও ফেডারেটেড সাইন-ইন সমাধানের (যেমন, Google-এর মাধ্যমে সাইন-ইন করুন) মতো একাধিক সাইন-ইন পদ্ধতিকে সমর্থন করে। Credential Manager API কল করা হলে, Android সিস্টেম ডিভাইসে ইনস্টল করা সব ক্রেডেনশিয়াল প্রোভাইডার থেকে ক্রেডেনশিয়াল একত্রিত করে। এই ডকুমেন্টটি API-এর সেট বর্ণনা করে যা এইসব ক্রেডেনশিয়াল প্রদানকারীর জন্য ইন্টিগ্রেশন এন্ডপয়েন্ট প্রদান করে।

সেট-আপ করুন

আপনার ক্রেডেনশিয়াল প্রোভাইডারে কার্যকারিতা প্রয়োগ করার আগে, নিম্নলিখিত বিভাগে দেখানো সেট-আপের ধাপগুলি সম্পূর্ণ করুন।

ডিপেন্ডেন্সি ঘোষণা করা

ক্রেডেনশিয়াল ম্যানেজার লাইব্রেরির লেটেস্ট ভার্সন ব্যবহার করতে, আপনার অ্যাপ মডিউলের বিল্ড স্ক্রিপ্টে নিম্নলিখিত ডিপেন্ডেন্সি যোগ করুন:

কোটলিন

dependencies {
    implementation("androidx.credentials:credentials:1.7.0-alpha03")
}

গ্রোভি

dependencies {
    implementation "androidx.credentials:credentials:1.7.0-alpha03"
}

ম্যানিফেস্ট ফাইলে পরিষেবা এলিমেন্ট ঘোষণা করুন

আপনার অ্যাপের ম্যানিফেস্ট ফাইলে AndroidManifest.xml, একটি <service> ঘোষণা অন্তর্ভুক্ত করুন যা androidx.credentials লাইব্রেরি থেকে CredentialProviderService ক্লাসকে প্রসারিত করে, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে।

<service android:name=".MyCredentialProviderService"
    android:enabled="true"
    android:exported="true"
    android:label="My Credential Provider"
    android:icon="@mipmap/ic_launcher"
    android:permission="android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE"
    tools:targetApi="upside_down_cake">
    <intent-filter>
        <action android:name="android.service.credentials.CredentialProviderService"/>
    </intent-filter>
    <meta-data
        android:name="android.credentials.provider"
        android:resource="@xml/provider"/>
</service>

আগের উদাহরণে দেখানো অনুমতি ও ইনটেন্ট ফিল্টার ক্রেডেনশিয়াল ম্যানেজার ফ্লো প্রত্যাশিতভাবে কাজ করার জন্য অবিচ্ছেদ্য। এই অনুমতি প্রয়োজন যাতে শুধুমাত্র Android সিস্টেম এই পরিষেবার সাথে বাইন্ড করতে পারে। ক্রেডেনশিয়াল ম্যানেজার দ্বারা ব্যবহার করার জন্য ক্রেডেনশিয়াল প্রদানকারী হিসেবে এই পরিষেবার ডিসকভারেবিলিটির জন্য ইনটেন্ট ফিল্টার ব্যবহার করা হয়।

কাজ করে এমন ক্রেডেনশিয়ালের ধরন ঘোষণা করুন

আপনার res/xml ডিরেক্টরিতে, provider.xml নামে একটি নতুন ফাইল তৈরি করুন। এই ফাইলে, লাইব্রেরিতে প্রতিটি ক্রেডেনশিয়াল ধরনের জন্য সংজ্ঞায়িত ধ্রুবকের মাধ্যমে আপনার পরিষেবা সমর্থন করে এমন ক্রেডেনশিয়াল ধরন ঘোষণা করুন। নিচের উদাহরণে, পরিষেবাটি চিরাচরিত পাসওয়ার্ডের পাশাপাশি পাসকীও সমর্থন করে, যার জন্য কনস্ট্যান্টগুলি TYPE_PASSWORD_CREDENTIAL এবং TYPE_PUBLIC_KEY_CREDENTIAL হিসেবে সংজ্ঞায়িত করা হয়েছে:

<credential-provider xmlns:android="http://schemas.android.com/apk/res/android">
    <capabilities>
        <capability name="android.credentials.TYPE_PASSWORD_CREDENTIAL" />
        <capability name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" />
    </capabilities>
</credential-provider>

আগের API লেভেলে, ক্রেডেনশিয়াল প্রদানকারীরা পাসওয়ার্ড ও অন্যান্য ডেটার জন্য অটোফিলের মতো API-এর সাথে ইন্টিগ্রেট করে। এইসব পরিষেবা প্রদানকারী একই ইন্টার্নাল ইনফ্রাস্ট্রাকচার ব্যবহার করে আগে থেকে থাকা ক্রেডেনশিয়ালের ধরন স্টোর করতে পারে এবং পাসকী সহ অন্যান্য ক্রেডেনশিয়াল স্টোর করার জন্য এটি আরও বাড়াতে পারে।

প্রোভাইডারের সাথে ইন্টার‍্যাকশনের জন্য দুই-পর্যায়ের পদ্ধতি

ক্রেডেনশিয়াল ম্যানেজার দুটি পর্যায়ে ক্রেডেনশিয়াল প্রদানকারীর সাথে ইন্টার‍্যাক্ট করে:

  1. প্রথম ধাপ হল শুরু/কোয়েরি ধাপ যেখানে সিস্টেম ক্রেডেনশিয়াল প্রোভাইডার পরিষেবার সাথে বাইন্ড করে এবং Begin… অনুরোধের সাথে onBeginGetCredentialRequest(), onBeginCreateCredentialRequest() অথবা onClearCredentialStateRequest() পদ্ধতিকে ইনভোক করে। প্রদানকারীকে অবশ্যই এইসব অনুরোধ প্রসেস করতে হবে এবং Begin…টি উত্তর দিতে হবে, অ্যাকাউন্ট সিলেক্টরে দেখানোর জন্য ভিজ্যুয়াল বিকল্পের প্রতিনিধিত্ব করে এমন এন্ট্রি দিয়ে সেগুলি পূরণ করতে হবে। প্রতিটি এন্ট্রিতে একটি PendingIntent সেট থাকতে হবে।
  2. ব্যবহারকারী কোনও এন্ট্রি বেছে নিলে, বেছে নেওয়ার পর্ব শুরু হয় এবং এন্ট্রির সাথে যুক্ত PendingIntent ফায়ার হয়ে যায়, যার ফলে সংশ্লিষ্ট প্রোভাইডার অ্যাক্টিভিটি শুরু হয়। ব্যবহারকারী এই অ্যাক্টিভিটির সাথে ইন্টার‍্যাক্ট করা শেষ করলে, ক্রেডেনশিয়াল প্রদানকারীকে অ্যাক্টিভিটি শেষ করার আগে অ্যাক্টিভিটির ফলাফলে উত্তর সেট করতে হবে। এই উত্তরটি তারপর ক্লায়েন্ট অ্যাপে পাঠানো হয় যা ক্রেডেনশিয়াল ম্যানেজারকে আহ্বান করে।

পাসকী তৈরি করা ম্যানেজ করুন

পাসকী তৈরি করার জন্য কোয়েরি ম্যানেজ করা

কোনও ক্লায়েন্ট অ্যাপ পাসকী তৈরি করতে এবং ক্রেডেনশিয়াল প্রোভাইডারের সাথে এটি স্টোর করতে চাইলে, তারা createCredential API কল করে। আপনার ক্রেডেনশিয়াল প্রদানকারী পরিষেবায় এই অনুরোধ ম্যানেজ করতে, যাতে পাসকী আপনার স্টোরেজে সেভ করা থাকে, তার জন্য নিম্নলিখিত বিভাগে দেখানো ধাপগুলি সম্পূর্ণ করুন।

  1. আপনার পরিষেবায় onBeginCreateCredentialRequest() পদ্ধতি ওভাররাইড করুন যা CredentialProviderService থেকে এক্সটেন্ড করা হয়েছে।
  2. BeginCreateCredentialRequest হ্যান্ডেল করতে, সংশ্লিষ্ট BeginCreateCredentialResponse তৈরি করুন এবং কলব্যাকের মাধ্যমে এটি পাস করুন।
  3. BeginCreateCredentialResponse তৈরি করার সময়, প্রয়োজনীয় CreateEntries যোগ করুন। প্রতিটি CreateEntry-এর সাথে এমন একটি অ্যাকাউন্ট সম্পর্কিত থাকতে হবে যেখানে ক্রেডেনশিয়াল সেভ করা যাবে এবং অন্যান্য প্রয়োজনীয় মেটাডেটার সাথে PendingIntent সেট করা থাকতে হবে।

নিম্নলিখিত উদাহরণ থেকে এইসব ধাপ কীভাবে প্রয়োগ করবেন তা জানুন।

override fun onBeginCreateCredentialRequest(
    request: BeginCreateCredentialRequest,
    cancellationSignal: CancellationSignal,
    callback: OutcomeReceiver<BeginCreateCredentialResponse, CreateCredentialException>,
) {
    val response: BeginCreateCredentialResponse? = processCreateCredentialRequest(request)
    if (response != null) {
        callback.onResult(response)
    } else {
        callback.onError(CreateCredentialUnknownException())
    }
}

fun processCreateCredentialRequest(request: BeginCreateCredentialRequest): BeginCreateCredentialResponse? {
    when (request) {
        is BeginCreatePublicKeyCredentialRequest -> {
            // Request is passkey type
            return handleCreatePasskeyQuery(request)
        }
    }
    // Request not supported
    return null
}

private fun handleCreatePasskeyQuery(
    request: BeginCreatePublicKeyCredentialRequest
): BeginCreateCredentialResponse {

    // Adding two create entries - one for storing credentials to the 'Personal'
    // account, and one for storing them to the 'Family' account. These
    // accounts are local to this sample app only.
    val createEntries: MutableList<CreateEntry> = mutableListOf()
    createEntries.add(
        CreateEntry(
            PERSONAL_ACCOUNT_ID,
            createNewPendingIntent(PERSONAL_ACCOUNT_ID, CREATE_PASSKEY_INTENT)
        )
    )

    createEntries.add(
        CreateEntry(
            FAMILY_ACCOUNT_ID,
            createNewPendingIntent(FAMILY_ACCOUNT_ID, CREATE_PASSKEY_INTENT)
        )
    )

    return BeginCreateCredentialResponse(createEntries)
}

private fun createNewPendingIntent(accountId: String, action: String): PendingIntent {
    val intent = Intent(action).setPackage(PACKAGE_NAME)

    // Add your local account ID as an extra to the intent, so that when
    // user selects this entry, the credential can be saved to this
    // account
    intent.putExtra(EXTRA_KEY_ACCOUNT_ID, accountId)

    return PendingIntent.getActivity(
        applicationContext, UNIQUE_REQ_CODE,
        intent,
        (
            PendingIntent.FLAG_MUTABLE
                or PendingIntent.FLAG_UPDATE_CURRENT
            )
    )
}

আপনার PendingIntent নির্মাণে নিম্নলিখিত বিষয়গুলি মেনে চলতে হবে:

  • প্রয়োজনীয় বায়োমেট্রিক প্রম্পট, কনফার্মেশন বা বাছাই দেখানোর জন্য সংশ্লিষ্ট অ্যাক্টিভিটি সেট-আপ করতে হবে।
  • সংশ্লিষ্ট অ্যাক্টিভিটি চালু করার সময় প্রদানকারীর প্রয়োজনীয় ডেটা, আপনার PendingIntent তৈরি করার জন্য ব্যবহৃত ইনটেন্টে অতিরিক্ত হিসেবে সেট করতে হবে, যেমন তৈরি করার ফ্লোতে accountId।
  • আপনার PendingIntent অবশ্যই PendingIntent.FLAG_MUTABLE ফ্ল্যাগ দিয়ে তৈরি করতে হবে যাতে সিস্টেম ইনটেন্ট এক্সট্রাতে চূড়ান্ত অনুরোধ যোগ করতে পারে।
  • আপনার PendingIntent-এ PendingIntent.FLAG_ONE_SHOT ফ্ল্যাগ থাকলে চলবে না, কারণ ব্যবহারকারী কোনও এন্ট্রি বেছে নিয়ে, ফিরে গিয়ে আবার সেটি বেছে নিতে পারেন, যার ফলে PendingIntent দু'বার ফায়ার হবে।
  • আপনার PendingIntent-এ অবশ্যই একটি অনন্য অনুরোধ কোড থাকতে হবে যাতে প্রতিটি এন্ট্রির নিজস্ব PendingIntent থাকতে পারে।

পাসকী তৈরির অনুরোধের জন্য এন্ট্রি বেছে নেওয়ার বিষয়টি ম্যানেজ করুন

  1. ব্যবহারকারী আগে থেকে পূরণ করা CreateEntry বেছে নিলে, সংশ্লিষ্ট PendingIntent চালু করা হয় এবং সংশ্লিষ্ট প্রদানকারী Activity তৈরি করা হয়।
  2. আপনার অ্যাক্টিভিটির onCreate পদ্ধতি চালু হওয়ার পরে, সংশ্লিষ্ট ইনটেন্ট অ্যাক্সেস করুন এবং ProviderCreateCredentialRequest পেতে PendingIntentHander ক্লাসে এটি পাস করুন।
  3. অনুরোধ থেকে requestJson, callingAppInfo এবং clientDataHash এক্সট্র্যাক্ট করুন।
  4. ইনটেন্ট এক্সট্রা থেকে স্থানীয় accountId এক্সট্র্যাক্ট করুন। এটি একটি স্যাম্পেল অ্যাপ নির্দিষ্ট প্রয়োগ এবং এর প্রয়োজন নেই। এই অ্যাকাউন্ট আইডি ব্যবহার করে এই নির্দিষ্ট অ্যাকাউন্ট আইডির জন্য এই ক্রেডেনশিয়াল সেভ করা যাবে।
  5. requestJson যাচাই করুন। নিচের উদাহরণে, ইনপুট JSON-কে WebAuthn স্পেসিফিকেশন অনুযায়ী স্ট্রাকচার্ড ক্লাসে পরিবর্তন করতে, PublicKeyCredentialCreationOptions-এর মতো লোকাল ডেটা ক্লাস ব্যবহার করা হয়। ক্রেডেনশিয়াল প্রোভাইডার হিসেবে, আপনি এটি নিজের পার্সার দিয়ে পরিবর্তন করতে পারবেন।
  6. কলটি যদি কোনও নেটিভ Android অ্যাপ থেকে করা হয়, তাহলে কলিং অ্যাপের জন্য অ্যাসেট-লিঙ্ক চেক করুন।
  7. যাচাইকরণ প্রম্পট দেখান। নিচের উদাহরণে Android Biometric API ব্যবহার করা হয়েছে।
  8. যাচাইকরণ সম্পূর্ণ হলে, একটি credentialId এবং একটি কী পেয়ার জেনারেট করুন।
  9. callingAppInfo.packageName-এর বিপরীতে আপনার লোকাল ডেটাবেসে ব্যক্তিগত কী সেভ করুন।
  10. Web Authentication API JSON রেসপন্স তৈরি করুন যাতে সর্বজনীন কী ও credentialId থাকে। নিচের উদাহরণে AuthenticatorAttestationResponse এবং FidoPublicKeyCredential-এর মতো লোকাল ইউটিলিটি ক্লাস ব্যবহার করা হয়েছে যা আগে উল্লেখ করা স্পেসিফিকেশনের ভিত্তিতে JSON তৈরি করতে সাহায্য করে। ক্রেডেনশিয়াল প্রদানকারী হিসেবে, আপনি এইসব ক্লাসকে আপনার নিজস্ব বিল্ডার দিয়ে পরিবর্তন করতে পারবেন।
  11. উপরে তৈরি করা JSON-এর সাহায্যে একটি CreatePublicKeyCredentialResponse তৈরি করো।
  12. CreatePublicKeyCredentialResponse-কে Intent-এ অতিরিক্ত হিসেবে সেট করুন PendingIntentHander.setCreateCredentialResponse()-এর মাধ্যমে এবং সেই ইনটেন্টকে অ্যাক্টিভিটির ফলাফল হিসেবে সেট করুন।
  13. অ্যাক্টিভিটি শেষ করুন।

নিচের কোডের উদাহরণে এই ধাপগুলি দেখানো হয়েছে। onCreate() চালু হয়ে গেলে, আপনার অ্যাক্টিভিটি ক্লাসে এই কোড ম্যানেজ করতে হবে।

override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
    super.onCreate(savedInstanceState, persistentState)
    // ...

    val request =
        PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)

    val accountId = intent.getStringExtra(CredentialsRepo.EXTRA_KEY_ACCOUNT_ID)
    if (request != null && request.callingRequest is CreatePublicKeyCredentialRequest) {
        val publicKeyRequest: CreatePublicKeyCredentialRequest =
            request.callingRequest as CreatePublicKeyCredentialRequest
        createPasskey(
            publicKeyRequest.requestJson,
            request.callingAppInfo,
            publicKeyRequest.clientDataHash,
            accountId
        )
    }
}

@SuppressLint("RestrictedApi")
fun createPasskey(
    requestJson: String,
    callingAppInfo: CallingAppInfo?,
    clientDataHash: ByteArray?,
    accountId: String?
) {
    val request = PublicKeyCredentialCreationOptions(requestJson)

    val biometricPrompt = BiometricPrompt(
        this,
        { }, // Pass in your own executor
        object : AuthenticationCallback() {
            override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                super.onAuthenticationError(errorCode, errString)
                finish()
            }

            override fun onAuthenticationFailed() {
                super.onAuthenticationFailed()
                finish()
            }

            @RequiresApi(VERSION_CODES.P)
            override fun onAuthenticationSucceeded(
                result: AuthenticationResult
            ) {
                super.onAuthenticationSucceeded(result)

                // Generate a credentialId
                val credentialId = ByteArray(32)
                SecureRandom().nextBytes(credentialId)

                // Generate a credential key pair
                val spec = ECGenParameterSpec("secp256r1")
                val keyPairGen = KeyPairGenerator.getInstance("EC")
                keyPairGen.initialize(spec)
                val keyPair = keyPairGen.genKeyPair()

                // Save passkey in your database as per your own implementation

                // Create AuthenticatorAttestationResponse object to pass to
                // FidoPublicKeyCredential

                val response = AuthenticatorAttestationResponse(
                    requestOptions = request,
                    credentialId = credentialId,
                    credentialPublicKey = getPublicKeyFromKeyPair(keyPair),
                    origin = appInfoToOrigin(callingAppInfo!!),
                    up = true,
                    uv = true,
                    be = true,
                    bs = true,
                    packageName = callingAppInfo.packageName
                )

                val credential = FidoPublicKeyCredential(
                    rawId = credentialId,
                    response = response,
                    authenticatorAttachment = "", // Add your authenticator attachment
                )
                val result = Intent()

                val createPublicKeyCredResponse =
                    CreatePublicKeyCredentialResponse(credential.json())

                // Set the CreateCredentialResponse as the result of the Activity
                PendingIntentHandler.setCreateCredentialResponse(
                    result,
                    createPublicKeyCredResponse
                )
                setResult(RESULT_OK, result)
                finish()
            }
        }
    )

    val promptInfo = BiometricPrompt.PromptInfo.Builder()
        .setTitle("Use your screen lock")
        .setSubtitle("Create passkey for ${request.rp.name}")
        .setAllowedAuthenticators(
            BiometricManager.Authenticators.BIOMETRIC_STRONG
            /* or BiometricManager.Authenticators.DEVICE_CREDENTIAL */
        )
        .build()
    biometricPrompt.authenticate(promptInfo)
}

@RequiresApi(VERSION_CODES.P)
fun appInfoToOrigin(info: CallingAppInfo): String {
    val cert = info.signingInfo.apkContentsSigners[0].toByteArray()
    val md = MessageDigest.getInstance("SHA-256")
    val certHash = md.digest(cert)
    // This is the format for origin
    return "android:apk-key-hash:${b64Encode(certHash)}"
}

পাসওয়ার্ড তৈরির অনুরোধের জন্য কোয়েরি ম্যানেজ করা

পাসওয়ার্ড তৈরির অনুরোধের জন্য কোয়েরি ম্যানেজ করতে, নিম্নলিখিত ধাপগুলি অনুসরণ করুন:

  • আগের বিভাগে উল্লেখ করা আপনার processCreateCredentialRequest() পদ্ধতির মধ্যে, পাসওয়ার্ড সংক্রান্ত অনুরোধ ম্যানেজ করার জন্য সুইচ ব্লকের মধ্যে আরেকটি কেস যোগ করুন।
  • BeginCreateCredentialResponse তৈরি করার সময়, প্রয়োজনীয় CreateEntries যোগ করুন।
  • প্রতিটি CreateEntry এমন একটি অ্যাকাউন্টের সাথে সম্পর্কিত হতে হবে যেখানে ক্রেডেনশিয়াল সেভ করা যাবে এবং অন্যান্য মেটাডেটার সাথে অবশ্যই একটি PendingIntent সেট করা থাকতে হবে।

এইসব ধাপ কীভাবে প্রয়োগ করবেন তা নিম্নলিখিত উদাহরণ থেকে দেখুন:

fun processCreateCredentialRequest(
    request: BeginCreateCredentialRequest
): BeginCreateCredentialResponse? {
    when (request) {
        is BeginCreatePublicKeyCredentialRequest -> {
            // Request is passkey type
            return handleCreatePasskeyQuery(request)
        }

        is BeginCreatePasswordCredentialRequest -> {
            // Request is password type
            return handleCreatePasswordQuery(request)
        }
    }
    return null
}

@RequiresApi(VERSION_CODES.M)
private fun handleCreatePasswordQuery(
    request: BeginCreatePasswordCredentialRequest
): BeginCreateCredentialResponse {
    val createEntries: MutableList<CreateEntry> = mutableListOf()

    // Adding two create entries - one for storing credentials to the 'Personal'
    // account, and one for storing them to the 'Family' account. These
    // accounts are local to this sample app only.
    createEntries.add(
        CreateEntry(
            PERSONAL_ACCOUNT_ID,
            createNewPendingIntent(PERSONAL_ACCOUNT_ID, CREATE_PASSWORD_INTENT)
        )
    )
    createEntries.add(
        CreateEntry(
            FAMILY_ACCOUNT_ID,
            createNewPendingIntent(FAMILY_ACCOUNT_ID, CREATE_PASSWORD_INTENT)
        )
    )

    return BeginCreateCredentialResponse(createEntries)
}

পাসওয়ার্ড তৈরির অনুরোধের জন্য এন্ট্রি বেছে নেওয়ার বিষয়টি ম্যানেজ করুন

ব্যবহারকারী কোনও পপুলেট করা CreateEntry বেছে নিলে, সংশ্লিষ্ট PendingIntent এক্সিকিউট হয় এবং অ্যাসোসিয়েট করা অ্যাক্টিভিটি দেখায়। onCreate-এ পাস করা সংশ্লিষ্ট ইনটেন্ট অ্যাক্সেস করুন এবং ProviderCreateCredentialRequest পদ্ধতি পেতে এটি PendingIntentHander ক্লাসে পাস করুন।

এই প্রসেস কীভাবে প্রয়োগ করতে হয় তা নিচের উদাহরণ থেকে বুঝতে পারবেন। এই কোডটি আপনার অ্যাক্টিভিটির onCreate() পদ্ধতিতে ম্যানেজ করতে হবে।

val createRequest = PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
val accountId = intent.getStringExtra(CredentialsRepo.EXTRA_KEY_ACCOUNT_ID)

if (createRequest == null) {
    return
}

val request: CreatePasswordRequest = createRequest.callingRequest as CreatePasswordRequest

// Fetch the ID and password from the request and save it in your database
mDatabase.addNewPassword(
    PasswordInfo(
        request.id,
        request.password,
        createRequest.callingAppInfo.packageName
    )
)

// Set the final response back
val result = Intent()
val response = CreatePasswordResponse()
PendingIntentHandler.setCreateCredentialResponse(result, response)
setResult(Activity.RESULT_OK, result)
finish()

ব্যবহারকারীর সাইন-ইন ম্যানেজ করা

ব্যবহারকারীর সাইন-ইন নিম্নলিখিত ধাপের মাধ্যমে ম্যানেজ করা হয়:

  • কোনও ক্লায়েন্ট অ্যাপ ব্যবহারকারীকে সাইন-ইন করানোর চেষ্টা করলে, এটি একটি GetCredentialRequest ইনস্ট্যান্স তৈরি করে।
  • Android ফ্রেমওয়ার্ক এই পরিষেবাগুলির সাথে বাইন্ড করার মাধ্যমে সব প্রযোজ্য ক্রেডেনশিয়াল প্রোভাইডারকে এই অনুরোধ পাঠায়।
  • প্রোভাইডার পরিষেবা তখন একটি BeginGetCredentialRequest পায় যার মধ্যে BeginGetCredentialOption-এর একটি তালিকা থাকে, যার প্রতিটিতে প্যারামিটার থাকে যা ম্যাচিং ক্রেডেনশিয়াল ফিরিয়ে আনতে ব্যবহার করা যেতে পারে।

আপনার ক্রেডেনশিয়াল প্রদানকারী পরিষেবায় এই অনুরোধ ম্যানেজ করতে, নিম্নলিখিত ধাপগুলি সম্পূর্ণ করুন:

  1. অনুরোধ ম্যানেজ করতে onBeginGetCredentialRequest() পদ্ধতি ওভাররাইড করুন। মনে রাখবেন যে আপনার ক্রেডেনশিয়াল লক করা থাকলে, আপনি অবিলম্বে উত্তরে একটি AuthenticationAction সেট করতে এবং কলব্যাক চালু করতে পারবেন।

    private val unlockEntryTitle = "Authenticate to continue"
    
    override fun onBeginGetCredentialRequest(
        request: BeginGetCredentialRequest,
        cancellationSignal: CancellationSignal,
        callback: OutcomeReceiver<BeginGetCredentialResponse, GetCredentialException>,
    ) {
        if (isAppLocked()) {
            callback.onResult(
                BeginGetCredentialResponse(
                    authenticationActions = mutableListOf(
                        AuthenticationAction(
                            unlockEntryTitle, createUnlockPendingIntent()
                        )
                    )
                )
            )
            return
        }
        try {
            response = processGetCredentialRequest(request)
            callback.onResult(response)
        } catch (e: GetCredentialException) {
            callback.onError(GetCredentialUnknownException())
        }
    }
    

    যেসব পরিষেবা প্রদানকারীর ক্ষেত্রে কোনও credentialEntries ফিরিয়ে দেওয়ার আগে ক্রেডেনশিয়াল আনলক করতে হয়, তাদের অবশ্যই একটি পেন্ডিং ইনটেন্ট সেট-আপ করতে হবে যা ব্যবহারকারীকে অ্যাপের আনলক ফ্লোতে নিয়ে যায়:

    private fun createUnlockPendingIntent(): PendingIntent {
        val intent = Intent(UNLOCK_INTENT).setPackage(PACKAGE_NAME)
        return PendingIntent.getActivity(
            applicationContext, UNIQUE_REQUEST_CODE, intent,
            (
                PendingIntent.FLAG_MUTABLE
                    or PendingIntent.FLAG_UPDATE_CURRENT
                )
        )
    }
    
  2. আপনার স্থানীয় ডেটাবেস থেকে ক্রেডেনশিয়াল ফিরিয়ে আনুন এবং CredentialEntries ব্যবহার করে সেগুলি সেট-আপ করুন যাতে সিলেক্টরে দেখানো যায়। পাসকী-র জন্য, আপনি ইনটেন্টে অতিরিক্ত হিসেবে credentialId সেট করতে পারেন যাতে ব্যবহারকারী এই এন্ট্রি বেছে নিলে এটি কোন ক্রেডেনশিয়ালের সাথে ম্যাপ করে তা জানা যায়।

    companion object {
        // These intent actions are specified for corresponding activities
        // that are to be invoked through the PendingIntent(s)
        private const val GET_PASSKEY_INTENT_ACTION = "PACKAGE_NAME.GET_PASSKEY"
        private const val GET_PASSWORD_INTENT_ACTION = "PACKAGE_NAME.GET_PASSWORD"
    }
    
    fun processGetCredentialRequest(
        request: BeginGetCredentialRequest
    ): BeginGetCredentialResponse {
        val callingPackageInfo = request.callingAppInfo
        val callingPackageName = callingPackageInfo?.packageName.orEmpty()
        val credentialEntries: MutableList<CredentialEntry> = mutableListOf()
    
        for (option in request.beginGetCredentialOptions) {
            when (option) {
                is BeginGetPasswordOption -> {
                    credentialEntries.addAll(
                        populatePasswordData(
                            callingPackageName,
                            option
                        )
                    )
                }
                is BeginGetPublicKeyCredentialOption -> {
                    credentialEntries.addAll(
                        populatePasskeyData(
                            callingPackageInfo,
                            option
                        )
                    )
                } else -> {
                    Log.i(TAG, "Request not supported")
                }
            }
        }
        return BeginGetCredentialResponse(credentialEntries)
    }
    
  3. আপনার ডেটাবেস থেকে ক্রেডেনশিয়াল কোয়েরি করুন, পূরণ করার জন্য পাসকী ও পাসওয়ার্ড এন্ট্রি তৈরি করুন।

    private fun populatePasskeyData(
        callingAppInfo: CallingAppInfo?,
        option: BeginGetPublicKeyCredentialOption
    ): List<CredentialEntry> {
        val passkeyEntries: MutableList<CredentialEntry> = mutableListOf()
        val request = PublicKeyCredentialRequestOptions(option.requestJson)
        // Get your credentials from database where you saved during creation flow
        val creds = getCredentialsFromInternalDb(request.rpId)
        val passkeys = creds.passkeys
        for (passkey in passkeys) {
            val data = Bundle()
            data.putString("credId", passkey.credId)
            passkeyEntries.add(
                PublicKeyCredentialEntry(
                    context = applicationContext,
                    username = passkey.username,
                    pendingIntent = createNewPendingIntent(
                        GET_PASSKEY_INTENT_ACTION,
                        data
                    ),
                    beginGetPublicKeyCredentialOption = option,
                    displayName = passkey.displayName,
                    icon = passkey.icon
                )
            )
        }
        return passkeyEntries
    }
    
    // Fetch password credentials and create password entries to populate to the user
    private fun populatePasswordData(
        callingPackage: String,
        option: BeginGetPasswordOption
    ): List<CredentialEntry> {
        val passwordEntries: MutableList<CredentialEntry> = mutableListOf()
    
        // Get your password credentials from database where you saved during
        // creation flow
        val creds = getCredentialsFromInternalDb(callingPackage)
        val passwords = creds.passwords
        for (password in passwords) {
            passwordEntries.add(
                PasswordCredentialEntry(
                    context = applicationContext,
                    username = password.username,
                    pendingIntent = createNewPendingIntent(
                        GET_PASSWORD_INTENT
                    ),
                    beginGetPasswordOption = option,
                    displayName = password.username,
                    icon = password.icon
                )
            )
        }
        return passwordEntries
    }
    
    private fun createNewPendingIntent(
        action: String,
        extra: Bundle? = null
    ): PendingIntent {
        val intent = Intent(action).setPackage(PACKAGE_NAME)
        if (extra != null) {
            intent.putExtra("CREDENTIAL_DATA", extra)
        }
    
        return PendingIntent.getActivity(
            applicationContext, UNIQUE_REQUEST_CODE, intent,
            (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
        )
    }
    
  4. ক্রেডেনশিয়াল কোয়েরি ও পপুলেট করার পরে, ব্যবহারকারীর বেছে নেওয়া ক্রেডেনশিয়ালের জন্য আপনাকে বেছে নেওয়ার পর্ব ম্যানেজ করতে হবে, সেটি পাসকী বা পাসওয়ার্ড যাই হোক না কেন।

'পাসকী'-এর জন্য ব্যবহারকারীর পছন্দ ম্যানেজ করা

  1. সংশ্লিষ্ট অ্যাক্টিভিটির onCreate পদ্ধতিতে, সংশ্লিষ্ট ইনটেন্ট ফিরিয়ে আনুন এবং PendingIntentHandler.retrieveProviderGetCredentialRequest()-এ পাস করুন।
  2. উপরে পাওয়া অনুরোধ থেকে GetPublicKeyCredentialOption এক্সট্র্যাক্ট করুন। এরপর, এই বিকল্প থেকে requestJson এবং clientDataHash এক্সট্র্যাক্ট করুন।
  3. ইনটেন্ট এক্সট্রা থেকে credentialId এক্সট্র্যাক্ট করুন, যা ক্রেডেনশিয়াল প্রদানকারী দ্বারা পপুলেট করা হয়েছিল যখন সংশ্লিষ্টPendingIntent সেট-আপ করা হয়েছিল।
  4. উপরে অ্যাক্সেস করা অনুরোধের প্যারামিটার ব্যবহার করে আপনার স্থানীয় ডেটাবেস থেকে পাসকী এক্সট্র্যাক্ট করুন।
  5. এক্সট্র্যাক্ট করা মেটাডেটা ও ব্যবহারকারী যাচাইকরণের মাধ্যমে পাসকীটি যে বৈধ তা নিশ্চিত করুন।

    val getRequest = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
    val publicKeyRequest = getRequest?.credentialOptions?.first() as GetPublicKeyCredentialOption
    
    val requestInfo = intent.getBundleExtra("CREDENTIAL_DATA")
    val credIdEnc = requestInfo?.getString("credId").orEmpty()
    
    // Get the saved passkey from your database based on the credential ID from the PublicKeyRequest
    val passkey = mDatabase.getPasskey(credIdEnc)
    
    // Decode the credential ID, private key and user ID
    val credId = b64Decode(credIdEnc)
    val privateKey = b64Decode(passkey.credPrivateKey)
    val uid = b64Decode(passkey.uid)
    
    val origin = appInfoToOrigin(getRequest.callingAppInfo)
    val packageName = getRequest.callingAppInfo.packageName
    
    validatePasskey(
        publicKeyRequest.requestJson,
        origin,
        packageName,
        uid,
        passkey.username,
        credId,
        privateKey
    )
    
  6. ব্যবহারকারীকে যাচাই করতে, একটি বায়োমেট্রিক প্রম্পট (বা অন্য কোনও অ্যাসারশন পদ্ধতি) দেখান। নিচে দেওয়া কোড স্নিপেট Android Biometric API ব্যবহার করে।

  7. যাচাইকরণ সম্পূর্ণ হয়ে গেলে, W3 Web Authentication Assertion spec-এর উপর ভিত্তি করে JSON রেসপন্স তৈরি করুন। নিচে দেওয়া কোড স্নিপেটে, AuthenticatorAssertionResponse-এর মতো সহায়ক ডেটা ক্লাস ব্যবহার করে স্ট্রাকচার্ড প্যারামিটার নেওয়া হয় এবং সেগুলিকে প্রয়োজনীয় JSON ফর্ম্যাটে কনভার্ট করা হয়। উত্তরে WebAuthn ক্রেডেনশিয়ালের ব্যক্তিগত কী থেকে পাওয়া ডিজিটাল স্বাক্ষর থাকে। সাইন-ইন করার আগে ব্যবহারকারীকে যাচাই করতে, রিলাইং পার্টির সার্ভার এই স্বাক্ষর যাচাই করতে পারে।

  8. উপরে জেনারেট করা JSON ব্যবহার করে একটি PublicKeyCredential তৈরি করুন এবং এটি একটি ফাইনাল GetCredentialResponse-এ সেট করুন। এই অ্যাক্টিভিটির ফলাফলের উপর এই চূড়ান্ত উত্তর সেট করুন।

এইসব ধাপ কীভাবে প্রয়োগ করা যেতে পারে তা নিম্নলিখিত উদাহরণ থেকে বোঝা যাবে:

val request = PublicKeyCredentialRequestOptions(requestJson)
val privateKey: ECPrivateKey = convertPrivateKey(privateKeyBytes)

val biometricPrompt = BiometricPrompt(
    this,
    { }, // Pass in your own executor
    object : BiometricPrompt.AuthenticationCallback() {
        override fun onAuthenticationError(
            errorCode: Int,
            errString: CharSequence
        ) {
            super.onAuthenticationError(errorCode, errString)
            finish()
        }

        override fun onAuthenticationFailed() {
            super.onAuthenticationFailed()
            finish()
        }

        override fun onAuthenticationSucceeded(
            result: BiometricPrompt.AuthenticationResult
        ) {
            super.onAuthenticationSucceeded(result)
            val response = AuthenticatorAssertionResponse(
                requestOptions = request,
                credentialId = credId,
                origin = origin,
                up = true,
                uv = true,
                be = true,
                bs = true,
                userHandle = uid,
                packageName = packageName
            )

            val sig = Signature.getInstance("SHA256withECDSA")
            sig.initSign(privateKey)
            sig.update(response.dataToSign())
            response.signature = sig.sign()

            val credential = FidoPublicKeyCredential(
                rawId = credId,
                response = response,
                authenticatorAttachment = "", // Add your authenticator attachment
            )
            val result = Intent()
            val passkeyCredential = PublicKeyCredential(credential.json())
            PendingIntentHandler.setGetCredentialResponse(
                result, GetCredentialResponse(passkeyCredential)
            )
            setResult(RESULT_OK, result)
            finish()
        }
    }
)

val promptInfo = BiometricPrompt.PromptInfo.Builder()
    .setTitle("Use your screen lock")
    .setSubtitle("Use passkey for ${request.rpId}")
    .setAllowedAuthenticators(
        BiometricManager.Authenticators.BIOMETRIC_STRONG
        /* or BiometricManager.Authenticators.DEVICE_CREDENTIAL */
    )
    .build()
biometricPrompt.authenticate(promptInfo)

পাসওয়ার্ড যাচাইকরণের জন্য ব্যবহারকারীর বেছে নেওয়া বিকল্প ম্যানেজ করা হচ্ছে

  1. আপনার সংশ্লিষ্ট অ্যাক্টিভিটিতে, onCreate-এ পাস করা ইনটেন্ট অ্যাক্সেস করুন এবং PendingIntentHandler ব্যবহার করে ProviderGetCredentialRequest এক্সট্র্যাক্ট করুন।
  2. ইনকামিং প্যাকেজের নামের জন্য পাসওয়ার্ড ক্রেডেনশিয়াল ফেরত পেতে অনুরোধে GetPasswordOption ব্যবহার করুন।

    val getRequest = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
    
    val passwordOption = getRequest?.credentialOptions?.first() as GetPasswordOption
    
    val username = passwordOption.allowedUserIds.first()
    // Fetch the credentials for the calling app package name
    val creds = mDatabase.getCredentials(callingAppInfo.packageName)
    val passwords = creds.passwords
    val it = passwords.iterator()
    var password = ""
    while (it.hasNext()) {
        val passwordItemCurrent = it.next()
        if (passwordItemCurrent.username == username) {
            password = passwordItemCurrent.password
            break
        }
    }
    
  3. উদ্ধার করা হয়ে গেলে, বেছে নেওয়া পাসওয়ার্ড ক্রেডেনশিয়ালের জন্য উত্তর সেট করুন।

    // Set the response back
    val result = Intent()
    val passwordCredential = PasswordCredential(username, password)
    PendingIntentHandler.setGetCredentialResponse(
        result, GetCredentialResponse(passwordCredential)
    )
    setResult(Activity.RESULT_OK, result)
    finish()
    

যাচাইকরণ অ্যাকশন এন্ট্রি বেছে নেওয়ার বিষয়টি ম্যানেজ করুন

আগে যেমন উল্লেখ করা হয়েছে, ক্রেডেনশিয়াল লক করা থাকলে ক্রেডেনশিয়াল প্রদানকারী একটি AuthenticationAction সেট করতে পারে। ব্যবহারকারী এই এন্ট্রি বেছে নিলে, PendingIntent-এ সেট করা ইনটেন্ট অ্যাকশনের সাথে সম্পর্কিত অ্যাক্টিভিটি ইনভোক করা হয়। ক্রেডেনশিয়াল প্রদানকারী তখন ক্রেডেনশিয়াল আনলক করার জন্য বায়োমেট্রিক যাচাইকরণ ফ্লো বা একই ধরনের মেকানিজম দেখাতে পারে। সফল হলে, ক্রেডেনশিয়াল প্রদানকারীকে অবশ্যই একটি BeginGetCredentialResponse তৈরি করতে হবে, ব্যবহারকারীর সাইন-ইন হ্যান্ডলিং যেভাবে উপরে বর্ণনা করা হয়েছে তার অনুরূপ, কারণ ক্রেডেনশিয়াল এখন আনলক করা হয়েছে। প্রস্তুত করা ইনটেন্ট ফলাফল হিসেবে সেট করা এবং অ্যাক্টিভিটি শেষ হওয়ার আগে PendingIntentHandler.setBeginGetCredentialResponse() পদ্ধতির মাধ্যমে এই উত্তর সেট করতে হবে।

ক্রেডেনশিয়াল সংক্রান্ত অনুরোধ মুছুন

কোনও ক্লায়েন্ট অ্যাপ ক্রেডেনশিয়াল বেছে নেওয়ার জন্য সেভ করা যেকোনও স্ট্যাটাস মুছে দেওয়ার অনুরোধ করতে পারে, যেমন, কোনও ক্রেডেনশিয়াল প্রদানকারী আগে বেছে নেওয়া ক্রেডেনশিয়াল মনে রাখতে পারে এবং পরের বার শুধু সেটিই দেখাতে পারে। কোনও ক্লায়েন্ট অ্যাপ এই API কল করে এবং আশা করে যে স্টিকি সিলেকশন মুছে ফেলা হবে। আপনার ক্রেডেনশিয়াল প্রদানকারী পরিষেবা onClearCredentialStateRequest() পদ্ধতি ওভাররাইড করে এই অনুরোধ ম্যানেজ করতে পারে:

override fun onClearCredentialStateRequest(
    request: ProviderClearCredentialStateRequest,
    cancellationSignal: CancellationSignal,
    callback: OutcomeReceiver<Void?, ClearCredentialException>
) {
    // Delete any maintained state as appropriate.
}

ব্যবহারকারীদের পাসওয়ার্ড, পাসকী ও অটোফিল স্ক্রিন থেকে আপনার পরিষেবা প্রদানকারীর সেটিংস খোলার অনুমতি দিতে, ক্রেডেনশিয়াল প্রদানকারী অ্যাপকে res/xml/provider.xml-এ credential-provider settingsActivity ম্যানিফেস্ট অ্যাট্রিবিউট প্রয়োগ করতে হবে। এই অ্যাট্রিবিউট আপনাকে একটি ইনটেন্ট ব্যবহার করতে দেয় যাতে ব্যবহারকারী পাসওয়ার্ড, পাসকী ও অটোফিল পরিষেবার তালিকায় কোনও পরিষেবা প্রদানকারীর নামে ক্লিক করলে আপনার অ্যাপের নিজস্ব সেটিংস স্ক্রিন খুলে যায়। এই অ্যাট্রিবিউটের ভ্যালু সেটিংস স্ক্রিন থেকে লঞ্চ করা অ্যাক্টিভিটির নামের সাথে সেট করুন।

<credential-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:settingsSubtitle="Example settings provider name"
    android:settingsActivity="com.example.SettingsActivity">
    <capabilities>
        <capability name="android.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" />
    </capabilities>
</credential-provider>
পরিবর্তন এবং খোলার বোতামের ফাংশন দেখানো ডায়াগ্রাম
ছবি ১: পরিবর্তন করুন বোতামটি আগে থেকে থাকা বেছে নেওয়ার ডায়ালগ খুলে দেয়, যার ফলে ব্যবহারকারী তার পছন্দের ক্রেডেনশিয়াল প্রদানকারী বেছে নিতে পারেন। খুলুন বোতামটি ম্যানিফেস্ট পরিবর্তনে সংজ্ঞায়িত সেটিংস অ্যাক্টিভিটি চালু করে এবং বিশেষভাবে সেই পরিষেবা প্রদানকারীর জন্য একটি সেটিংস পৃষ্ঠা খোলে।

সেটিংস ইনটেন্ট

সেটিংস খোলা: android.settings.CREDENTIAL_PROVIDER ইনটেন্ট একটি সেটিংস স্ক্রিন দেখায় যেখানে ব্যবহারকারী তার পছন্দের এবং অতিরিক্ত ক্রেডেনশিয়াল প্রদানকারী বেছে নিতে পারেন।

পাসওয়ার্ড, পাসকী ও অটোফিল সেটিংস স্ক্রিন
ছবি ২: পাসওয়ার্ড, পাসকী ও অটোফিল সেটিংস স্ক্রিন।

পছন্দের ক্রেডেনশিয়াল পরিষেবা: ACTION_REQUEST_SET_AUTOFILL_SERVICE ইনটেন্ট আপনার ব্যবহারকারীকে পছন্দের পরিষেবা প্রদানকারী বেছে নেওয়ার স্ক্রিনে রিডাইরেক্ট করে। এই স্ক্রিনে বেছে নেওয়া পরিষেবা প্রদানকারী পছন্দের ক্রেডেনশিয়াল ও অটোফিল পরিষেবা প্রদানকারী হয়ে যায়।

পরিবর্তন এবং খোলার বোতামের ফাংশন দেখানো ডায়াগ্রাম
ছবি ৩: পাসওয়ার্ড, পাসকী ও অটোফিল সেটিংসের জন্য পছন্দের পরিষেবা স্ক্রিন।

বিশেষ অধিকারপ্রাপ্ত অ্যাপের অনুমোদিত তালিকা পান

ওয়েব ব্রাউজারের মতো বিশেষ সুবিধা প্রাপ্ত অ্যাপ, ক্রেডেনশিয়াল ম্যানেজারের GetCredentialRequest() এবং CreatePublicKeyCredentialRequest() পদ্ধতিতে origin প্যারামিটার সেট করে অন্যান্য নির্ভরশীল পক্ষের হয়ে ক্রেডেনশিয়াল ম্যানেজার কল করে। এইসব অনুরোধ প্রসেস করতে, ক্রেডেনশিয়াল প্রোভাইডার getOrigin() API ব্যবহার করে origin ফিরিয়ে আনে।

origin ফিরিয়ে আনতে, ক্রেডেনশিয়াল প্রদানকারী অ্যাপকে androidx.credentials.provider.CallingAppInfo's getOrigin() API-তে বিশেষাধিকারপ্রাপ্ত ও বিশ্বস্ত কলারের তালিকা পাস করতে হবে। এই অনুমোদিত তালিকা অবশ্যই একটি সঠিক JSON অবজেক্ট হতে হবে। origin ফেরত দেওয়া হয় যদি packageName এবং signingInfo থেকে পাওয়া সার্টিফিকেট ফিঙ্গারপ্রিন্ট getOrigin() API-তে পাস করা privilegedAllowlist-এ পাওয়া অ্যাপের সাথে মিলে যায়। origin ভ্যালু পাওয়ার পরে, প্রোভাইডার অ্যাপের এটিকে একটি বিশেষাধিকার প্রাপ্ত কল হিসেবে বিবেচনা করা উচিত এবং কলিং অ্যাপের সিগনেচার ব্যবহার করে origin গণনা করার পরিবর্তে AuthenticatorResponse-এ origin ক্লায়েন্ট ডেটাতে সেট করা উচিত।

আপনি origin ফিরিয়ে আনলে, clientDataHash ব্যবহার করুন যা সরাসরি CreatePublicKeyCredentialRequest() বা GetPublicKeyCredentialOption()-এ দেওয়া আছে, স্বাক্ষর অনুরোধের সময় clientDataJSON অ্যাসেম্বল ও হ্যাশ করার পরিবর্তে। JSON পার্সিং সংক্রান্ত সমস্যা এড়াতে, অ্যাটেস্টেশন ও অ্যাসারশন উত্তরে clientDataJSON-এর জন্য একটি প্লেসহোল্ডার ভ্যালু সেট করুন। Google Password Manager, getOrigin()-এ কল করার জন্য সর্বজনীনভাবে উপলভ্য অনুমতি তালিকা ব্যবহার করে। ক্রেডেনশিয়াল প্রদানকারী হিসেবে, আপনি এই তালিকা ব্যবহার করতে পারেন অথবা API-এর মাধ্যমে বর্ণিত JSON ফর্ম্যাটে আপনার নিজস্ব তালিকা প্রদান করতে পারেন। কোন তালিকা ব্যবহার করা হবে তা বেছে নেওয়ার দায়িত্ব প্রদানকারীর। থার্ড-পার্টি ক্রেডেনশিয়াল প্রদানকারীর সাথে বিশেষ অ্যাক্সেস পেতে, থার্ড-পার্টির প্রদান করা ডকুমেন্টেশন দেখুন।

কোনও ডিভাইসে পরিষেবা প্রদানকারী চালু করুন

ব্যবহারকারীকে অবশ্যই ডিভাইস সেটিংস > পাসওয়ার্ড ও অ্যাকাউন্ট > আপনার পরিষেবা প্রদানকারী > চালু বা বন্ধ করুন বিকল্পের মাধ্যমে পরিষেবা প্রদানকারীকে চালু করতে হবে।

fun createSettingsPendingIntent(): PendingIntent