<lambda>null1 package com.android.onboarding.bedsteadonboarding.contractutils
2
3 import android.content.Context
4 import android.os.Process
5 import android.util.Log
6 import com.android.onboarding.bedsteadonboarding.permissions.TestPermissions
7 import com.android.onboarding.bedsteadonboarding.providers.ConfigProviderUtil
8 import com.android.onboarding.bedsteadonboarding.providers.ConfigProviderUtil.TEST_NODE_CLASS_COLUMN
9
10 private const val TAG = "TestFramework"
11
12 /**
13 * Contains helper methods which different nodes can call to check if they are allowed to execute.
14 */
15 object ContractExecutionEligibilityChecker {
16
17 private val isRunningOnDebuggableDevice: Boolean by lazy {
18 TestPermissions.isRunningOnDebuggableDevice()
19 }
20 internal val ALLOW_ALL_NODES = null
21
22 /**
23 * Checks if the test configurations are set. If it is not set then node is allowed to execute.
24 * This will always be the case when the node is being executed in production. If it is set then
25 * it means that test process have set those configurations. So it would then check if the node is
26 * being allowed to execute. If not then it will terminate the app process.
27 *
28 * @param context context of the package being executed.
29 * @param contractIdentifier contract identifier of the node which is to be executed obtainable
30 * via [ContractUtils].
31 */
32 fun terminateIfNodeIsTriggeredByTestAndIsNotAllowed(
33 context: Context,
34 contractIdentifier: String,
35 ) {
36 try {
37 // Fetch the list of contracts which are allowed to execute.
38 val allowedNodes = getAllowedNodes(context)
39
40 if (allowedNodes == ALLOW_ALL_NODES) {
41 return
42 }
43
44 val nodeToCheck = contractIdentifier
45
46 // If the node is attempted to be executed as part of test but it is not allowed.
47 if (nodeToCheck !in allowedNodes) {
48 Log.w(TAG, "Contract $nodeToCheck is not allowed to execute")
49 // Kill the app under test.
50 Process.killProcess(Process.myPid())
51 }
52 } catch (t: Throwable) {
53 // For safety, ignore the exception since current function would run in production flow.
54 Log.e(TAG, "Error while fetching list of allowed nodes", t)
55 }
56 }
57
58 fun terminateIfNodeIsTriggeredByTestAndIsNotAllowed(context: Context, contractClass: Class<*>) =
59 terminateIfNodeIsTriggeredByTestAndIsNotAllowed(
60 context,
61 ContractUtils.getContractIdentifier(contractClass),
62 )
63
64 /**
65 * Fetches the list of contractClasses of nodes which are allowed to execute. It will always
66 * return [ALLOW_ALL_NODES] when the node is being executed in production.
67 */
68 internal fun getAllowedNodes(context: Context): Set<String>? {
69 if (!isRunningOnDebuggableDevice) return ALLOW_ALL_NODES
70
71 val uri = ConfigProviderUtil.getTestConfigUri(context)
72 var allowedNodes: MutableSet<String>? = null
73 // Fetch the list of contracts which are allowed to execute.
74 context.contentResolver
75 .query(
76 uri,
77 arrayOf(TEST_NODE_CLASS_COLUMN),
78 /* selection= */ null,
79 /* selectionArgs= */ null,
80 /* sortOrder= */ null,
81 /* cancellationSignal= */ null,
82 )
83 .use { cursor ->
84 if ((cursor != null) && cursor.moveToFirst()) {
85 do {
86 val columnIndex = cursor.getColumnIndex(TEST_NODE_CLASS_COLUMN)
87 require(columnIndex != -1) { "Column $TEST_NODE_CLASS_COLUMN not found." }
88 val allowedNode = cursor.getString(columnIndex)
89 if (allowedNodes == null) {
90 allowedNodes = mutableSetOf()
91 }
92 allowedNodes!!.add(allowedNode)
93 } while (cursor.moveToNext())
94 }
95 }
96 return allowedNodes?.toSet() ?: ALLOW_ALL_NODES
97 }
98 }
99