<lambda>null1 package com.android.onboarding.bedsteadonboarding.providers
2
3 import android.content.ContentProvider
4 import android.content.ContentValues
5 import android.content.Context
6 import android.content.UriMatcher
7 import android.content.pm.ProviderInfo
8 import android.database.Cursor
9 import android.database.MatrixCursor
10 import android.net.Uri
11 import android.os.Binder
12 import android.os.Build
13 import android.os.Bundle
14 import android.util.Log
15 import com.android.onboarding.bedsteadonboarding.data.NodeData
16 import com.android.onboarding.bedsteadonboarding.data.TestConfigData
17 import com.android.onboarding.bedsteadonboarding.fakes.FakeActivityNodeHelper
18 import com.android.onboarding.bedsteadonboarding.permissions.TestPermissions
19 import com.android.onboarding.bedsteadonboarding.providers.ConfigProviderUtil.TEST_NODE_CLASS_COLUMN
20 import com.android.onboarding.contracts.ContractResult
21
22 /**
23 * Content Provider used for storing all the test configs. Each production onboarding app will have
24 * an instance of this provider to store test configuration to be used when such app is executing
25 * during tests. Only the test process has permissions to insert and delete the configurations
26 * whereas all onboarding apps can query even outside of tests.
27 */
28 class TestContentProvider : ContentProvider() {
29 private lateinit var baseTestConfigUri: Uri
30
31 private val testConfigUriMatcher = UriMatcher(UriMatcher.NO_MATCH)
32
33 private var testConfigData = TestConfigData(listOf())
34 private var fakeActivityNodesConfig = mapOf<String, ContractResult>()
35
36 override fun attachInfo(context: Context?, info: ProviderInfo) {
37 super.attachInfo(context, info)
38 val authority = info.authority
39 baseTestConfigUri = ConfigProviderUtil.getBaseContentUri(authority)
40 testConfigUriMatcher.addURI(authority, ConfigProviderUtil.TEST_CONFIG_PATH, IS_TEST_CONFIG_KEY)
41 testConfigUriMatcher.addURI(
42 authority,
43 ConfigProviderUtil.FAKE_ACTIVITY_NODE_CONFIG_QUERY_PATH,
44 IS_FAKE_ACTIVITY_NODE_CONFIG_QUERY_KEY,
45 )
46 testConfigUriMatcher.addURI(
47 authority,
48 ConfigProviderUtil.FAKE_ACTIVITY_NODE_CONFIG_INSERT_PATH,
49 IS_FAKE_ACTIVITY_NODE_CONFIG_INSERT_KEY,
50 )
51 }
52
53 override fun onCreate(): Boolean {
54 return true
55 }
56
57 override fun query(
58 uri: Uri,
59 projection: Array<String>?,
60 selection: String?,
61 selectionArgs: Array<String>?,
62 sortOrder: String?,
63 ): Cursor {
64 when (testConfigUriMatcher.match(uri)) {
65 IS_TEST_CONFIG_KEY -> {
66 val matrixCursor = MatrixCursor(arrayOf(TEST_NODE_CLASS_COLUMN))
67 for (nodeData in testConfigData.testNodes) {
68 matrixCursor.newRow().add(TEST_NODE_CLASS_COLUMN, nodeData.allowedContractIdentifier)
69 }
70 return matrixCursor
71 }
72 IS_FAKE_ACTIVITY_NODE_CONFIG_QUERY_KEY -> {
73 val matrixCursor = MatrixCursor(arrayOf())
74 val contractIdentifier = uri.lastPathSegment
75 if (contractIdentifier != null) {
76 val contractResult = fakeActivityNodesConfig[contractIdentifier]
77 if (contractResult != null) {
78 matrixCursor.extras =
79 FakeActivityNodeHelper.createBundleFromContractResultAndIdentifier(
80 contractResult,
81 contractIdentifier,
82 )
83 }
84 }
85 return matrixCursor
86 }
87 else -> throw UnsupportedOperationException("Unsupported Query Uri $uri")
88 }
89 }
90
91 override fun getType(uri: Uri): String? {
92 throw UnsupportedOperationException("getType")
93 }
94
95 override fun insert(uri: Uri, values: ContentValues?): Uri {
96 throw UnsupportedOperationException("insert")
97 }
98
99 override fun insert(uri: Uri, values: ContentValues?, extras: Bundle?): Uri? {
100 if (checkPermissionOrThrow()) {
101 when (testConfigUriMatcher.match(uri)) {
102 IS_FAKE_ACTIVITY_NODE_CONFIG_INSERT_KEY -> {
103 if (extras == null) {
104 Log.e(TAG, "Fake activity node config passed is null while inserting")
105 return Uri.EMPTY
106 }
107 val contractIdentifierAndResult =
108 FakeActivityNodeHelper.extractContractIdentifierAndContractResultFromBundle(extras)
109 ?: error("contract identifier in bundle in null")
110 val mutableFakeActivityNodesConfig = fakeActivityNodesConfig.toMutableMap()
111 mutableFakeActivityNodesConfig[contractIdentifierAndResult.first] =
112 contractIdentifierAndResult.second
113 fakeActivityNodesConfig = mutableFakeActivityNodesConfig.toMap()
114 }
115 else -> throw UnsupportedOperationException("Unsupported Insertion Uri $uri")
116 }
117 }
118 return Uri.EMPTY
119 }
120 override fun bulkInsert(uri: Uri, contentValuesList: Array<ContentValues>): Int {
121 if (checkPermissionOrThrow()) {
122 val nodeConfigList = mutableListOf<NodeData>()
123 when (testConfigUriMatcher.match(uri)) {
124 IS_TEST_CONFIG_KEY -> {
125 for (contentValues in contentValuesList) {
126 contentValues.getAsString(TEST_NODE_CLASS_COLUMN)?.let { testNodeClassName ->
127 nodeConfigList.add(NodeData(testNodeClassName))
128 }
129 }
130 }
131 else -> throw UnsupportedOperationException("Unsupported Insertion Uri $uri")
132 }
133 testConfigData = TestConfigData(nodeConfigList.toList())
134 return nodeConfigList.size
135 }
136 return 0
137 }
138
139 override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
140 if (checkPermissionOrThrow()) {
141 when (testConfigUriMatcher.match(uri)) {
142 IS_TEST_CONFIG_KEY -> {
143 testConfigData = TestConfigData(listOf())
144 fakeActivityNodesConfig = mapOf()
145 }
146 else -> throw UnsupportedOperationException("Unsupported Deletion Uri $uri")
147 }
148 return 1
149 }
150 return 0
151 }
152
153 override fun update(
154 uri: Uri,
155 values: ContentValues?,
156 selection: String?,
157 selectionArgs: Array<String>?,
158 ): Int {
159 throw UnsupportedOperationException("update")
160 }
161
162 private fun checkPermissionOrThrow(): Boolean {
163 if (hasPermission()) return true
164 throw SecurityException("Does not have permission")
165 }
166
167 private fun hasPermission(): Boolean {
168 val context = context
169 return when {
170 "robolectric" == Build.FINGERPRINT -> true
171 context != null ->
172 TestPermissions.canCallerExecuteTestFunctionality(context, Binder.getCallingUid())
173 else -> {
174 Log.e(TAG, "Can't check test permission since context is null")
175 false
176 }
177 }
178 }
179
180 companion object {
181 private const val IS_TEST_CONFIG_KEY = 1
182 private const val IS_FAKE_ACTIVITY_NODE_CONFIG_QUERY_KEY = 2
183 private const val IS_FAKE_ACTIVITY_NODE_CONFIG_INSERT_KEY = 3
184 private const val TAG = "TestContentProvider"
185 }
186 }
187