1 /*
2 * Copyright (C) 2022 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.settingslib.spa.debug
18
19 import android.content.ContentProvider
20 import android.content.ContentValues
21 import android.content.Context
22 import android.content.Intent
23 import android.content.Intent.URI_INTENT_SCHEME
24 import android.content.UriMatcher
25 import android.content.pm.ProviderInfo
26 import android.database.Cursor
27 import android.database.MatrixCursor
28 import android.net.Uri
29 import android.util.Log
30 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
31 import com.android.settingslib.spa.framework.util.KEY_DESTINATION
32 import com.android.settingslib.spa.framework.util.KEY_HIGHLIGHT_ENTRY
33 import com.android.settingslib.spa.framework.util.KEY_SESSION_SOURCE_NAME
34 import com.android.settingslib.spa.framework.util.SESSION_BROWSE
35 import com.android.settingslib.spa.framework.util.SESSION_SEARCH
36 import com.android.settingslib.spa.framework.util.createIntent
37
38 private const val TAG = "DebugProvider"
39
40 /**
41 * The content provider to return debug data.
42 * One can query the provider result by:
43 * $ adb shell content query --uri content://<AuthorityPath>/<QueryPath>
44 * For gallery, AuthorityPath = com.android.spa.gallery.debug
45 * Some examples:
46 * $ adb shell content query --uri content://<AuthorityPath>/page_debug
47 * $ adb shell content query --uri content://<AuthorityPath>/entry_debug
48 * $ adb shell content query --uri content://<AuthorityPath>/page_info
49 * $ adb shell content query --uri content://<AuthorityPath>/entry_info
50 */
51 class DebugProvider : ContentProvider() {
52 private val spaEnvironment get() = SpaEnvironmentFactory.instance
53 private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
54
deletenull55 override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
56 TODO("Implement this to handle requests to delete one or more rows")
57 }
58
getTypenull59 override fun getType(uri: Uri): String? {
60 TODO(
61 "Implement this to handle requests for the MIME type of the data" +
62 "at the given URI"
63 )
64 }
65
insertnull66 override fun insert(uri: Uri, values: ContentValues?): Uri? {
67 TODO("Implement this to handle requests to insert a new row.")
68 }
69
updatenull70 override fun update(
71 uri: Uri,
72 values: ContentValues?,
73 selection: String?,
74 selectionArgs: Array<String>?
75 ): Int {
76 TODO("Implement this to handle requests to update one or more rows.")
77 }
78
onCreatenull79 override fun onCreate(): Boolean {
80 Log.d(TAG, "onCreate")
81 return true
82 }
83
attachInfonull84 override fun attachInfo(context: Context?, info: ProviderInfo?) {
85 if (info != null) {
86 QueryEnum.PAGE_DEBUG_QUERY.addUri(uriMatcher, info.authority)
87 QueryEnum.ENTRY_DEBUG_QUERY.addUri(uriMatcher, info.authority)
88 QueryEnum.PAGE_INFO_QUERY.addUri(uriMatcher, info.authority)
89 QueryEnum.ENTRY_INFO_QUERY.addUri(uriMatcher, info.authority)
90 }
91 super.attachInfo(context, info)
92 }
93
querynull94 override fun query(
95 uri: Uri,
96 projection: Array<String>?,
97 selection: String?,
98 selectionArgs: Array<String>?,
99 sortOrder: String?
100 ): Cursor? {
101 return try {
102 when (uriMatcher.match(uri)) {
103 QueryEnum.PAGE_DEBUG_QUERY.queryMatchCode -> queryPageDebug()
104 QueryEnum.ENTRY_DEBUG_QUERY.queryMatchCode -> queryEntryDebug()
105 QueryEnum.PAGE_INFO_QUERY.queryMatchCode -> queryPageInfo()
106 QueryEnum.ENTRY_INFO_QUERY.queryMatchCode -> queryEntryInfo()
107 else -> throw UnsupportedOperationException("Unknown Uri $uri")
108 }
109 } catch (e: UnsupportedOperationException) {
110 throw e
111 } catch (e: Exception) {
112 Log.e(TAG, "Provider querying exception:", e)
113 null
114 }
115 }
116
queryPageDebugnull117 private fun queryPageDebug(): Cursor {
118 val entryRepository by spaEnvironment.entryRepository
119 val cursor = MatrixCursor(QueryEnum.PAGE_DEBUG_QUERY.getColumns())
120 for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
121 val page = pageWithEntry.page
122 if (!page.isBrowsable()) continue
123 val command = createBrowseAdbCommand(
124 destination = page.buildRoute(),
125 sessionName = SESSION_BROWSE
126 )
127 if (command != null) {
128 cursor.newRow().add(ColumnEnum.PAGE_START_ADB.id, command)
129 }
130 }
131 return cursor
132 }
133
queryEntryDebugnull134 private fun queryEntryDebug(): Cursor {
135 val entryRepository by spaEnvironment.entryRepository
136 val cursor = MatrixCursor(QueryEnum.ENTRY_DEBUG_QUERY.getColumns())
137 for (entry in entryRepository.getAllEntries()) {
138 val page = entry.containerPage()
139 if (!page.isBrowsable()) continue
140 val command = createBrowseAdbCommand(
141 destination = page.buildRoute(),
142 entryId = entry.id,
143 sessionName = SESSION_SEARCH
144 )
145 if (command != null) {
146 cursor.newRow().add(ColumnEnum.ENTRY_START_ADB.id, command)
147 }
148 }
149 return cursor
150 }
151
queryPageInfonull152 private fun queryPageInfo(): Cursor {
153 val entryRepository by spaEnvironment.entryRepository
154 val cursor = MatrixCursor(QueryEnum.PAGE_INFO_QUERY.getColumns())
155 for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
156 val page = pageWithEntry.page
157 val intent = page.createIntent(SESSION_BROWSE) ?: Intent()
158 cursor.newRow()
159 .add(ColumnEnum.PAGE_ID.id, page.id)
160 .add(ColumnEnum.PAGE_NAME.id, page.displayName)
161 .add(ColumnEnum.PAGE_ROUTE.id, page.buildRoute())
162 .add(ColumnEnum.PAGE_INTENT_URI.id, intent.toUri(URI_INTENT_SCHEME))
163 .add(ColumnEnum.PAGE_ENTRY_COUNT.id, pageWithEntry.entries.size)
164 .add(ColumnEnum.PAGE_BROWSABLE.id, if (page.isBrowsable()) 1 else 0)
165 }
166 return cursor
167 }
168
queryEntryInfonull169 private fun queryEntryInfo(): Cursor {
170 val entryRepository by spaEnvironment.entryRepository
171 val cursor = MatrixCursor(QueryEnum.ENTRY_INFO_QUERY.getColumns())
172 for (entry in entryRepository.getAllEntries()) {
173 val intent = entry.createIntent(SESSION_SEARCH) ?: Intent()
174 cursor.newRow()
175 .add(ColumnEnum.ENTRY_ID.id, entry.id)
176 .add(ColumnEnum.ENTRY_LABEL.id, entry.label)
177 .add(ColumnEnum.ENTRY_ROUTE.id, entry.containerPage().buildRoute())
178 .add(ColumnEnum.ENTRY_INTENT_URI.id, intent.toUri(URI_INTENT_SCHEME))
179 .add(
180 ColumnEnum.ENTRY_HIERARCHY_PATH.id,
181 entryRepository.getEntryPathWithLabel(entry.id)
182 )
183 }
184 return cursor
185 }
186 }
187
createBrowseAdbCommandnull188 private fun createBrowseAdbCommand(
189 destination: String? = null,
190 entryId: String? = null,
191 sessionName: String? = null,
192 ): String? {
193 val context = SpaEnvironmentFactory.instance.appContext
194 val browseActivityClass = SpaEnvironmentFactory.instance.browseActivityClass ?: return null
195 val packageName = context.packageName
196 val activityName = browseActivityClass.name.replace(packageName, "")
197 val destinationParam =
198 if (destination != null) " -e $KEY_DESTINATION $destination" else ""
199 val highlightParam =
200 if (entryId != null) " -e $KEY_HIGHLIGHT_ENTRY $entryId" else ""
201 val sessionParam =
202 if (sessionName != null) " -e $KEY_SESSION_SOURCE_NAME $sessionName" else ""
203 return "adb shell am start -n $packageName/$activityName" +
204 "$destinationParam$highlightParam$sessionParam"
205 }
206