1 /*
<lambda>null2  * Copyright (C) 2021 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 @file:Suppress("DEPRECATION")
17 
18 package com.android.permissioncontroller.permission.ui.auto
19 
20 import android.annotation.SuppressLint
21 import android.content.Intent
22 import android.os.Bundle
23 import android.os.UserHandle
24 import androidx.lifecycle.ViewModelProvider
25 import androidx.preference.Preference
26 import androidx.preference.PreferenceCategory
27 import androidx.preference.PreferenceGroup
28 import com.android.car.ui.preference.CarUiPreference
29 import com.android.permissioncontroller.Constants
30 import com.android.permissioncontroller.DumpableLog
31 import com.android.permissioncontroller.PermissionControllerStatsLog
32 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_CLICKED
33 import com.android.permissioncontroller.PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__REVIEW_DECISION
34 import com.android.permissioncontroller.PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__SCREEN_VIEWED
35 import com.android.permissioncontroller.PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__VIEW_ALL_CLICKED
36 import com.android.permissioncontroller.R
37 import com.android.permissioncontroller.auto.AutoSettingsFrameFragment
38 import com.android.permissioncontroller.auto.DrivingDecisionReminderService
39 import com.android.permissioncontroller.permission.data.v33.PermissionDecision
40 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity
41 import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionDecisionsViewModel
42 import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionDecisionsViewModelFactory
43 import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageUid
44 import com.android.permissioncontroller.permission.utils.Utils
45 import kotlin.math.min
46 
47 /** Shows summary of recent permission decisions. */
48 @SuppressLint("NewApi")
49 class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() {
50 
51     companion object {
52         const val EXTRA_SOURCE = "source"
53         const val EXTRA_SOURCE_NOTIFICATION = "notification"
54         private const val LOG_TAG = "AutoReviewPermissionDecisionsFragment"
55         private const val MAX_DECISIONS = 3
56 
57         /** Creates a new instance of [AutoReviewPermissionDecisionsFragment]. */
58         fun newInstance(
59             sessionId: Long,
60             userHandle: UserHandle,
61             source: String?
62         ): AutoReviewPermissionDecisionsFragment {
63             return AutoReviewPermissionDecisionsFragment().apply {
64                 arguments =
65                     Bundle().apply {
66                         putLong(Constants.EXTRA_SESSION_ID, sessionId)
67                         putParcelable(Intent.EXTRA_USER, userHandle)
68                         putString(EXTRA_SOURCE, source)
69                     }
70             }
71         }
72     }
73 
74     private lateinit var user: UserHandle
75     private lateinit var viewModel: ReviewPermissionDecisionsViewModel
76     private lateinit var recentPermissionsGroup: PreferenceCategory
77     private var sessionId: Long = Constants.INVALID_SESSION_ID
78 
79     override fun onCreate(savedInstanceState: Bundle?) {
80         super.onCreate(savedInstanceState)
81         if (arguments == null) {
82             DumpableLog.e(LOG_TAG, "Missing arguments")
83             activity?.finish()
84             return
85         }
86         if (!requireArguments().containsKey(Intent.EXTRA_USER)) {
87             DumpableLog.e(LOG_TAG, "Missing argument ${Intent.EXTRA_USER}")
88             activity?.finish()
89             return
90         }
91         if (!requireArguments().containsKey(Constants.EXTRA_SESSION_ID)) {
92             DumpableLog.e(LOG_TAG, "Missing argument ${Constants.EXTRA_SESSION_ID}")
93             activity?.finish()
94             return
95         }
96         user = requireArguments().getParcelable<UserHandle>(Intent.EXTRA_USER)!!
97         sessionId = requireArguments().getLong(Constants.EXTRA_SESSION_ID)
98         if (
99             requireArguments().containsKey(EXTRA_SOURCE) &&
100                 (requireArguments().getString(EXTRA_SOURCE) == EXTRA_SOURCE_NOTIFICATION)
101         ) {
102             DrivingDecisionReminderService.cancelNotification(requireActivity())
103             logDecisionReminderNotificationClicked()
104         }
105         val factory =
106             ReviewPermissionDecisionsViewModelFactory(requireActivity().getApplication()!!, user)
107         viewModel = ViewModelProvider(this, factory)[ReviewPermissionDecisionsViewModel::class.java]
108 
109         addPrivacyDashboardPreference()
110         addPermissionManagerPreference()
111         preferenceScreen.addPreference(AutoDividerPreference(context))
112         recentPermissionsGroup =
113             PreferenceCategory(context!!).apply {
114                 title = getString(R.string.review_permission_decisions)
115             }
116         preferenceScreen.addPreference(recentPermissionsGroup)
117 
118         viewModel.recentPermissionDecisionsLiveData.observe(this) { recentDecisions ->
119             onRecentDecisionsChanged(recentDecisions)
120         }
121         headerLabel = getString(R.string.app_permissions)
122 
123         logScreenViewed()
124     }
125 
126     override fun onCreatePreferences(bundle: Bundle?, s: String?) {
127         preferenceScreen = preferenceManager.createPreferenceScreen(context!!)
128     }
129 
130     private fun onRecentDecisionsChanged(recentDecisions: List<PermissionDecision>) {
131         recentPermissionsGroup.removeAll()
132 
133         if (recentDecisions.isEmpty()) {
134             addNoRecentDecisionsPreference(recentPermissionsGroup)
135         } else {
136             addRecentDecisionPreferences(recentPermissionsGroup, recentDecisions)
137         }
138         if (recentDecisions.size > MAX_DECISIONS) {
139             addViewAllPreference(recentPermissionsGroup)
140         }
141     }
142 
143     private fun addPrivacyDashboardPreference() {
144         val preference =
145             CarUiPreference(context).apply {
146                 title = getString(R.string.permission_usage_title)
147                 summary = getString(R.string.auto_permission_usage_summary)
148                 onPreferenceClickListener =
149                     Preference.OnPreferenceClickListener { _ ->
150                         val intent =
151                             Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).apply {
152                                 putExtra(Constants.EXTRA_SESSION_ID, sessionId)
153                             }
154                         startActivity(intent)
155                         true
156                     }
157             }
158         preferenceScreen.addPreference(preference)
159     }
160 
161     private fun addPermissionManagerPreference() {
162         val preference =
163             CarUiPreference(context).apply {
164                 title = getString(R.string.app_permission_manager)
165                 summary = getString(R.string.auto_permission_manager_summary)
166                 onPreferenceClickListener =
167                     Preference.OnPreferenceClickListener { _ ->
168                         val intent =
169                             Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply {
170                                 putExtra(Intent.EXTRA_USER, user)
171                                 putExtra(
172                                     ManagePermissionsActivity.EXTRA_CALLER_NAME,
173                                     javaClass.name
174                                 )
175                                 putExtra(Constants.EXTRA_SESSION_ID, sessionId)
176                             }
177                         startActivity(intent)
178                         true
179                     }
180             }
181         preferenceScreen.addPreference(preference)
182     }
183 
184     private fun addRecentDecisionPreferences(
185         preferenceGroup: PreferenceGroup,
186         recentDecisions: List<PermissionDecision>
187     ) {
188         for (i in 0 until min(recentDecisions.size, MAX_DECISIONS)) {
189             val recentDecision = recentDecisions[i]
190             val decisionPreference =
191                 CarUiPreference(context).apply {
192                     icon = viewModel.getAppIcon(recentDecision.packageName)
193                     title = viewModel.createPreferenceTitle(recentDecision)
194                     summary = viewModel.createSummaryText(recentDecision)
195                     onPreferenceClickListener =
196                         Preference.OnPreferenceClickListener {
197                             viewModel.createManageAppPermissionIntent(recentDecision).also {
198                                 startActivity(it)
199                             }
200                             logPermissionDecisionClicked(
201                                 recentDecision.packageName,
202                                 recentDecision.permissionGroupName
203                             )
204                             true
205                         }
206                 }
207             preferenceGroup.addPreference(decisionPreference)
208         }
209     }
210 
211     private fun addViewAllPreference(preferenceGroup: PreferenceGroup) {
212         val viewAllIcon = requireContext().getDrawable(R.drawable.car_ic_apps)
213         val preference =
214             CarUiPreference(context).apply {
215                 icon = Utils.applyTint(context, viewAllIcon, android.R.attr.colorControlNormal)
216                 title = getString(R.string.review_permission_decisions_view_all)
217                 onPreferenceClickListener =
218                     Preference.OnPreferenceClickListener {
219                         val frag =
220                             AutoReviewPermissionDecisionsViewAllFragment.newInstance(
221                                 sessionId,
222                                 user
223                             )
224                         getParentFragmentManager()
225                             .beginTransaction()
226                             .replace(android.R.id.content, frag)
227                             .addToBackStack(null)
228                             .commit()
229                         logViewAllClicked()
230                         true
231                     }
232             }
233         preferenceGroup.addPreference(preference)
234     }
235 
236     private fun addNoRecentDecisionsPreference(preferenceGroup: PreferenceGroup) {
237         val preference =
238             CarUiPreference(context).apply {
239                 title = getString(R.string.review_permission_decisions_empty)
240             }
241         preferenceGroup.addPreference(preference)
242     }
243 
244     private fun logScreenViewed() {
245         PermissionControllerStatsLog.write(
246             PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED,
247             sessionId,
248             RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__SCREEN_VIEWED,
249             0,
250             null
251         )
252     }
253 
254     private fun logViewAllClicked() {
255         PermissionControllerStatsLog.write(
256             PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED,
257             sessionId,
258             RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__VIEW_ALL_CLICKED,
259             0,
260             null
261         )
262     }
263 
264     private fun logPermissionDecisionClicked(packageName: String, permissionGroupName: String) {
265         val uid = getPackageUid(requireActivity().getApplication(), packageName, user) ?: return
266         PermissionControllerStatsLog.write(
267             PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED,
268             sessionId,
269             RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__REVIEW_DECISION,
270             uid,
271             permissionGroupName
272         )
273     }
274 
275     private fun logDecisionReminderNotificationClicked() {
276         PermissionControllerStatsLog.write(
277             PermissionControllerStatsLog.PERMISSION_REMINDER_NOTIFICATION_INTERACTED,
278             sessionId,
279             PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_CLICKED
280         )
281     }
282 }
283