1 /*
<lambda>null2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.statementservice.domain
18 
19 import android.content.Context
20 import android.content.Intent
21 import android.content.pm.verify.domain.DomainVerificationManager
22 import android.content.pm.verify.domain.DomainVerificationRequest
23 import android.os.UserManager
24 import androidx.work.BackoffPolicy
25 import androidx.work.ExistingWorkPolicy
26 import androidx.work.WorkManager
27 import com.android.statementservice.domain.worker.SingleV2RequestWorker
28 import com.android.statementservice.utils.component1
29 import com.android.statementservice.utils.component2
30 import com.android.statementservice.utils.component3
31 
32 import java.time.Duration
33 
34 /**
35  * Handles [DomainVerificationRequest]s from the system, which indicates a package on the device
36  * has domains which require verification against a server side assetlinks.json file, allowing the
37  * app to resolve web [Intent]s.
38  *
39  * This will delegate to v1 or v2 depending on the received broadcast and which components are
40  * enabled. See [DomainVerificationManager] for the full API.
41  */
42 open class DomainVerificationReceiverV2 : BaseDomainVerificationReceiver() {
43 
44     companion object {
45 
46         private const val ENABLE_V2 = true
47 
48         /**
49          * Toggle to always re-verify packages that this receiver is notified of. This means on
50          * every package change, even previously successful requests are re-sent. Generally only
51          * for debugging.
52          */
53         @Suppress("SimplifyBooleanWithConstants")
54         private const val ALWAYS_VERIFY = false || DEBUG
55 
56         private const val PACKAGE_WORK_PREFIX_V2 = "package_request_v2-"
57     }
58 
59     override val tag = DomainVerificationReceiverV2::class.java.simpleName
60 
61     override fun onReceive(context: Context, intent: Intent) {
62         when (intent.action) {
63             Intent.ACTION_DOMAINS_NEED_VERIFICATION -> {
64                 // If the user isn't unlocked yet, the request will be ignored, as WorkManager
65                 // cannot schedule workers when the user data directories are encrypted.
66                 if (context.getSystemService(UserManager::class.java)?.isUserUnlocked == true) {
67                     scheduleUnlockedV2(context, intent)
68                 }
69             }
70             else -> debugLog { "Received invalid broadcast: $intent" }
71         }
72     }
73 
74     private fun scheduleUnlockedV2(context: Context, intent: Intent) {
75         if (!ENABLE_V2) {
76             return
77         }
78 
79         val manager = context.getSystemService(DomainVerificationManager::class.java) ?: return
80         val workManager = WorkManager.getInstance(context)
81 
82         val request = intent.getParcelableExtra<DomainVerificationRequest>(
83             DomainVerificationManager.EXTRA_VERIFICATION_REQUEST
84         ) ?: return
85 
86         debugLog { "Attempting v2 verification for ${request.packageNames}" }
87 
88         request.packageNames.forEach { packageName ->
89             val (domainSetId, _, hostToStateMap) = manager.getDomainVerificationInfo(packageName)
90                 ?: return@forEach
91 
92             val workRequests = hostToStateMap
93                 .filterValues {
94                     // TODO(b/159952358): Should we support re-query? There's no good way to
95                     //  signal to an AOSP implementation from an entity's website about when
96                     //  to re-query, unless it's just done on each update.
97                     // AOSP implementation does not support re-query
98                     ALWAYS_VERIFY || VerifyStatus.shouldRetry(it)
99                 }
100                 .map { (host, _) ->
101                     SingleV2RequestWorker.buildRequest(domainSetId, packageName, host) {
102                         setConstraints(networkConstraints)
103                         setBackoffCriteria(BackoffPolicy.EXPONENTIAL, Duration.ofHours(1))
104                     }
105                 }
106 
107             if (workRequests.isNotEmpty()) {
108                 workManager.beginUniqueWork(
109                     "$PACKAGE_WORK_PREFIX_V2$packageName",
110                     ExistingWorkPolicy.REPLACE, workRequests
111                 )
112                     .enqueue()
113             }
114         }
115     }
116 }
117