• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.permissioncontroller.permission.ui.model
18 
19 import android.Manifest
20 import android.annotation.SuppressLint
21 import android.app.AppOpsManager
22 import android.app.AppOpsManager.MODE_ALLOWED
23 import android.app.AppOpsManager.MODE_IGNORED
24 import android.app.AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
25 import android.apphibernation.AppHibernationManager
26 import android.content.Context
27 import android.os.Bundle
28 import android.os.UserHandle
29 import android.util.Log
30 import androidx.fragment.app.Fragment
31 import androidx.lifecycle.ViewModel
32 import androidx.lifecycle.ViewModelProvider
33 import androidx.navigation.fragment.findNavController
34 import com.android.modules.utils.build.SdkLevel
35 import com.android.permission.flags.Flags
36 import com.android.permissioncontroller.DeviceUtils
37 import com.android.permissioncontroller.PermissionControllerApplication
38 import com.android.permissioncontroller.PermissionControllerStatsLog
39 import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION
40 import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_DISABLED
41 import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_ENABLED
42 import com.android.permissioncontroller.R
43 import com.android.permissioncontroller.hibernation.isHibernationEnabled
44 import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData
45 import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData
46 import com.android.permissioncontroller.permission.data.HibernationSettingStateLiveData
47 import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData
48 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData
49 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData.Companion.NON_RUNTIME_NORMAL_PERMS
50 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
51 import com.android.permissioncontroller.permission.data.get
52 import com.android.permissioncontroller.permission.data.v35.PackagePermissionsExternalDeviceLiveData
53 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState
54 import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage
55 import com.android.permissioncontroller.permission.ui.Category
56 import com.android.permissioncontroller.permission.utils.IPC
57 import com.android.permissioncontroller.permission.utils.PermissionMapping
58 import com.android.permissioncontroller.permission.utils.Utils
59 import com.android.permissioncontroller.permission.utils.Utils.AppPermsLastAccessType
60 import com.android.permissioncontroller.permission.utils.navigateSafe
61 import com.android.permissioncontroller.permission.utils.v35.MultiDeviceUtils
62 import java.time.Instant
63 import java.util.concurrent.TimeUnit
64 import kotlin.math.max
65 import kotlinx.coroutines.GlobalScope
66 import kotlinx.coroutines.launch
67 
68 /**
69  * ViewModel for the AppPermissionGroupsFragment. Has a liveData with the UI information for all
70  * permission groups that this package requests runtime permissions from
71  *
72  * @param packageName The name of the package this viewModel is representing
73  * @param user The user of the package this viewModel is representing
74  */
75 class AppPermissionGroupsViewModel(
76     private val packageName: String,
77     private val user: UserHandle,
78     private val sessionId: Long
79 ) : ViewModel() {
80 
81     companion object {
82         const val AGGREGATE_DATA_FILTER_BEGIN_DAYS_1 = 1
83         const val AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 = 7
84         val LOG_TAG: String = AppPermissionGroupsViewModel::class.java.simpleName
85     }
86 
87     val app = PermissionControllerApplication.get()!!
88 
89     enum class PermSubtitle(val value: Int) {
90         NONE(0),
91         MEDIA_ONLY(1),
92         ALL_FILES(2),
93         FOREGROUND_ONLY(3),
94         BACKGROUND(4),
95     }
96 
97     data class GroupUiInfo(
98         val groupName: String,
99         val isSystem: Boolean = false,
100         val subtitle: PermSubtitle,
101         val persistentDeviceId: String,
102     ) {
103         constructor(
104             groupName: String,
105             isSystem: Boolean
106         ) : this(
107             groupName,
108             isSystem,
109             PermSubtitle.NONE,
110             MultiDeviceUtils.getDefaultDevicePersistentDeviceId()
111         )
112 
113         constructor(
114             groupName: String,
115             isSystem: Boolean,
116             subtitle: PermSubtitle,
117         ) : this(
118             groupName,
119             isSystem,
120             subtitle,
121             MultiDeviceUtils.getDefaultDevicePersistentDeviceId()
122         )
123     }
124 
125     // Auto-revoke and hibernation share the same settings
126     val autoRevokeLiveData = HibernationSettingStateLiveData[packageName, user]
127 
128     private val packagePermsLiveData = PackagePermissionsLiveData[packageName, user]
129     private val appPermGroupUiInfoLiveDatas = mutableMapOf<String, AppPermGroupUiInfoLiveData>()
130     private val fullStoragePermsLiveData = FullStoragePermissionAppsLiveData
131     private val packagePermsExternalDeviceLiveData =
132         PackagePermissionsExternalDeviceLiveData[packageName, user]
133     private val appOpsManager = app.getSystemService(AppOpsManager::class.java)!!
134     private val packageManager = app.packageManager
135 
136     /** Check if the application is in restricted settings mode. */
137     @SuppressLint("NewApi")
138     fun isClearRestrictedAllowed(): Boolean {
139         if (Flags.enhancedConfirmationBackportEnabled()) {
140             // TODO(b/347876543): Replace this when EnhancedConfirmtionServiceImpl is
141             // available.
142             val isRestricted =
143                 appOpsManager.noteOpNoThrow(
144                     AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS,
145                     packageManager.getApplicationInfoAsUser(packageName, 0, user).uid,
146                     packageName,
147                     null,
148                     null
149                 ) == MODE_IGNORED
150             return isRestricted
151         }
152         return false
153     }
154 
155     /** Allow restricted settings on the applications. */
156     @SuppressLint("NewApi")
157     fun clearRestriction() {
158         if (Flags.enhancedConfirmationBackportEnabled()) {
159             // TODO(b/347876543): Replace this when EnhancedConfirmationServiceImpl is
160             // available.
161             appOpsManager.setMode(
162                 AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS,
163                 packageManager.getApplicationInfoAsUser(packageName, 0, user).uid,
164                 packageName,
165                 MODE_ALLOWED
166             )
167         }
168     }
169 
170     /**
171      * LiveData whose data is a map of grant category (either allowed or denied) to a list of
172      * permission group names that match the key, and two booleans representing if this is a system
173      * group, and a subtitle resource ID, if applicable.
174      */
175     val packagePermGroupsLiveData =
176         object :
177             SmartUpdateMediatorLiveData<@JvmSuppressWildcards Map<Category, List<GroupUiInfo>>>() {
178 
179             init {
180                 addSource(packagePermsLiveData) { update() }
181                 addSource(fullStoragePermsLiveData) { update() }
182                 addSource(autoRevokeLiveData) {
183                     removeSource(autoRevokeLiveData)
184                     update()
185                 }
186                 addSource(packagePermsExternalDeviceLiveData) { update() }
187                 update()
188             }
189 
190             override fun onUpdate() {
191                 val groups =
192                     packagePermsLiveData.value?.keys?.filter { it != NON_RUNTIME_NORMAL_PERMS }
193                 if (groups == null && packagePermsLiveData.isInitialized) {
194                     value = null
195                     return
196                 } else if (
197                     groups == null ||
198                         (Manifest.permission_group.STORAGE in groups &&
199                             !fullStoragePermsLiveData.isInitialized) ||
200                         !autoRevokeLiveData.isInitialized
201                 ) {
202                     return
203                 }
204 
205                 val getLiveData = { groupName: String ->
206                     AppPermGroupUiInfoLiveData[packageName, groupName, user]
207                 }
208                 setSourcesToDifference(groups, appPermGroupUiInfoLiveDatas, getLiveData)
209 
210                 if (!appPermGroupUiInfoLiveDatas.all { it.value.isInitialized }) {
211                     return
212                 }
213 
214                 val groupGrantStates = mutableMapOf<Category, MutableList<GroupUiInfo>>()
215                 groupGrantStates[Category.ALLOWED] = mutableListOf()
216                 groupGrantStates[Category.ASK] = mutableListOf()
217                 groupGrantStates[Category.DENIED] = mutableListOf()
218 
219                 val fullStorageState =
220                     fullStoragePermsLiveData.value?.find { pkg ->
221                         pkg.packageName == packageName && pkg.user == user
222                     }
223 
224                 for (groupName in groups) {
225                     val isSystem =
226                         PermissionMapping.getPlatformPermissionGroups().contains(groupName)
227                     appPermGroupUiInfoLiveDatas[groupName]?.value?.let { uiInfo ->
228                         if (SdkLevel.isAtLeastT() && !uiInfo.shouldShow) {
229                             return@let
230                         }
231                         if (
232                             groupName == Manifest.permission_group.STORAGE &&
233                                 (fullStorageState?.isGranted == true && !fullStorageState.isLegacy)
234                         ) {
235                             groupGrantStates[Category.ALLOWED]!!.add(
236                                 GroupUiInfo(groupName, isSystem, PermSubtitle.ALL_FILES)
237                             )
238                             return@let
239                         }
240                         when (uiInfo.permGrantState) {
241                             PermGrantState.PERMS_ALLOWED -> {
242                                 val subtitle =
243                                     if (groupName == Manifest.permission_group.STORAGE) {
244                                         if (SdkLevel.isAtLeastT()) {
245                                             PermSubtitle.NONE
246                                         } else {
247                                             if (fullStorageState?.isLegacy == true) {
248                                                 PermSubtitle.ALL_FILES
249                                             } else {
250                                                 PermSubtitle.MEDIA_ONLY
251                                             }
252                                         }
253                                     } else {
254                                         PermSubtitle.NONE
255                                     }
256                                 groupGrantStates[Category.ALLOWED]!!.add(
257                                     GroupUiInfo(groupName, isSystem, subtitle)
258                                 )
259                             }
260                             PermGrantState.PERMS_ALLOWED_ALWAYS ->
261                                 groupGrantStates[Category.ALLOWED]!!.add(
262                                     GroupUiInfo(groupName, isSystem, PermSubtitle.BACKGROUND)
263                                 )
264                             PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY ->
265                                 groupGrantStates[Category.ALLOWED]!!.add(
266                                     GroupUiInfo(groupName, isSystem, PermSubtitle.FOREGROUND_ONLY)
267                                 )
268                             PermGrantState.PERMS_DENIED ->
269                                 groupGrantStates[Category.DENIED]!!.add(
270                                     GroupUiInfo(groupName, isSystem)
271                                 )
272                             PermGrantState.PERMS_ASK ->
273                                 groupGrantStates[Category.ASK]!!.add(
274                                     GroupUiInfo(groupName, isSystem)
275                                 )
276                         }
277                     }
278                 }
279 
280                 packagePermsExternalDeviceLiveData.value?.forEach { externalDeviceGrantInfo ->
281                     val groupName = externalDeviceGrantInfo.groupName
282                     val isSystem =
283                         PermissionMapping.getPlatformPermissionGroups().contains(groupName)
284                     val persistentDeviceId = externalDeviceGrantInfo.persistentDeviceId
285                     when (externalDeviceGrantInfo.permGrantState) {
286                         PermGrantState.PERMS_ALLOWED -> {
287                             groupGrantStates[Category.ALLOWED]!!.add(
288                                 GroupUiInfo(
289                                     groupName,
290                                     isSystem,
291                                     PermSubtitle.NONE,
292                                     persistentDeviceId
293                                 )
294                             )
295                         }
296                         PermGrantState.PERMS_ALLOWED_ALWAYS ->
297                             groupGrantStates[Category.ALLOWED]!!.add(
298                                 GroupUiInfo(
299                                     groupName,
300                                     isSystem,
301                                     PermSubtitle.BACKGROUND,
302                                     persistentDeviceId
303                                 )
304                             )
305                         PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY ->
306                             groupGrantStates[Category.ALLOWED]!!.add(
307                                 GroupUiInfo(
308                                     groupName,
309                                     isSystem,
310                                     PermSubtitle.FOREGROUND_ONLY,
311                                     persistentDeviceId
312                                 )
313                             )
314                         PermGrantState.PERMS_DENIED ->
315                             groupGrantStates[Category.DENIED]!!.add(
316                                 GroupUiInfo(
317                                     groupName,
318                                     isSystem,
319                                     PermSubtitle.NONE,
320                                     persistentDeviceId
321                                 )
322                             )
323                         PermGrantState.PERMS_ASK ->
324                             groupGrantStates[Category.ASK]!!.add(
325                                 GroupUiInfo(
326                                     groupName,
327                                     isSystem,
328                                     PermSubtitle.NONE,
329                                     persistentDeviceId
330                                 )
331                             )
332                     }
333                 }
334 
335                 value = groupGrantStates
336             }
337         }
338 
339     // TODO 206455664: remove once issue is identified
340     fun logLiveDataState() {
341         Log.i(
342             LOG_TAG,
343             "Overall liveData isStale: ${packagePermGroupsLiveData.isStale}, " +
344                 "isInitialized: ${packagePermGroupsLiveData.isInitialized}, " +
345                 "value: ${packagePermGroupsLiveData.value}"
346         )
347         Log.i(
348             LOG_TAG,
349             "AutoRevoke liveData isStale: ${autoRevokeLiveData.isStale}, " +
350                 "isInitialized: ${autoRevokeLiveData.isInitialized}, " +
351                 "value: ${autoRevokeLiveData.value}"
352         )
353         Log.i(
354             LOG_TAG,
355             "PackagePerms liveData isStale: ${packagePermsLiveData.isStale}, " +
356                 "isInitialized: ${packagePermsLiveData.isInitialized}, " +
357                 "value: ${packagePermsLiveData.value}"
358         )
359         Log.i(
360             LOG_TAG,
361             "FullStorage liveData isStale: ${fullStoragePermsLiveData.isStale}, " +
362                 "isInitialized: ${fullStoragePermsLiveData.isInitialized}, " +
363                 "value size: ${fullStoragePermsLiveData.value?.size}"
364         )
365         for ((group, liveData) in appPermGroupUiInfoLiveDatas) {
366             Log.i(
367                 LOG_TAG,
368                 "$group ui liveData isStale: ${liveData.isStale}, " +
369                     "isInitialized: ${liveData.isInitialized}, " +
370                     "value size: ${liveData.value}"
371             )
372         }
373     }
374 
375     fun setAutoRevoke(enabled: Boolean) {
376         GlobalScope.launch(IPC) {
377             val aom = app.getSystemService(AppOpsManager::class.java)!!
378             val lightPackageInfo = LightPackageInfoLiveData[packageName, user].getInitializedValue()
379 
380             if (lightPackageInfo != null) {
381                 Log.i(
382                     LOG_TAG,
383                     "sessionId $sessionId setting auto revoke enabled to $enabled for" +
384                         "$packageName $user"
385                 )
386                 val tag =
387                     if (enabled) {
388                         APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_ENABLED
389                     } else {
390                         APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_DISABLED
391                     }
392                 PermissionControllerStatsLog.write(
393                     APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION,
394                     sessionId,
395                     lightPackageInfo.uid,
396                     packageName,
397                     tag
398                 )
399 
400                 val mode =
401                     if (enabled) {
402                         MODE_ALLOWED
403                     } else {
404                         MODE_IGNORED
405                     }
406                 aom.setUidMode(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, lightPackageInfo.uid, mode)
407                 if (isHibernationEnabled() && SdkLevel.isAtLeastSv2() && !enabled) {
408                     // Only unhibernate on S_V2+ to have consistent toggle behavior w/ Settings
409                     val ahm = app.getSystemService(AppHibernationManager::class.java)!!
410                     ahm.setHibernatingForUser(packageName, false)
411                     ahm.setHibernatingGlobally(packageName, false)
412                 }
413             }
414         }
415     }
416 
417     fun showExtraPerms(fragment: Fragment, args: Bundle) {
418         fragment.findNavController().navigateSafe(R.id.perm_groups_to_custom, args)
419     }
420 
421     fun showAllPermissions(fragment: Fragment, args: Bundle) {
422         fragment.findNavController().navigateSafe(R.id.perm_groups_to_all_perms, args)
423     }
424 
425     // This method should be consolidated with
426     // PermissionAppsViewModel#extractGroupUsageLastAccessTime
427     fun extractGroupUsageLastAccessTime(
428         accessTime: MutableMap<String, Long>,
429         appPermissionUsages: List<AppPermissionUsage>,
430         packageName: String
431     ) {
432         if (!SdkLevel.isAtLeastS()) {
433             return
434         }
435 
436         val aggregateDataFilterBeginDays =
437             if (DeviceUtils.isHandheld()) AGGREGATE_DATA_FILTER_BEGIN_DAYS_7
438             else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1
439 
440         accessTime.clear()
441         val filterTimeBeginMillis =
442             max(
443                 System.currentTimeMillis() -
444                     TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays.toLong()),
445                 Instant.EPOCH.toEpochMilli()
446             )
447         val numApps: Int = appPermissionUsages.size
448         for (appIndex in 0 until numApps) {
449             val appUsage: AppPermissionUsage = appPermissionUsages[appIndex]
450             if (appUsage.packageName != packageName) {
451                 continue
452             }
453             val appGroups = appUsage.groupUsages
454             val numGroups = appGroups.size
455             for (groupIndex in 0 until numGroups) {
456                 val groupUsage = appGroups[groupIndex]
457                 var lastAccessTime = groupUsage.lastAccessTime
458                 val groupName = groupUsage.group.name
459                 if (lastAccessTime == 0L || lastAccessTime < filterTimeBeginMillis) {
460                     continue
461                 }
462 
463                 // We might have another AppPermissionUsage entry that's of the same packageName
464                 // but with a different uid. In that case, we want to grab the max lastAccessTime
465                 // as the last usage to show.
466                 lastAccessTime =
467                     Math.max(
468                         accessTime.getOrDefault(groupName, Instant.EPOCH.toEpochMilli()),
469                         lastAccessTime
470                     )
471                 accessTime[groupName] = lastAccessTime
472             }
473         }
474     }
475 
476     fun getPreferenceSummary(
477         groupInfo: GroupUiInfo,
478         context: Context,
479         lastAccessTime: Long?
480     ): String {
481         val summaryTimestamp =
482             Utils.getPermissionLastAccessSummaryTimestamp(
483                 lastAccessTime,
484                 context,
485                 groupInfo.groupName
486             )
487         @AppPermsLastAccessType val lastAccessType: Int = summaryTimestamp.second
488 
489         return when (groupInfo.subtitle) {
490             PermSubtitle.BACKGROUND ->
491                 when (lastAccessType) {
492                     Utils.LAST_24H_CONTENT_PROVIDER ->
493                         context.getString(R.string.app_perms_content_provider_24h_background)
494                     Utils.LAST_7D_CONTENT_PROVIDER ->
495                         context.getString(R.string.app_perms_content_provider_7d_background)
496                     Utils.LAST_24H_SENSOR_TODAY ->
497                         context.getString(
498                             R.string.app_perms_24h_access_background,
499                             summaryTimestamp.first
500                         )
501                     Utils.LAST_24H_SENSOR_YESTERDAY ->
502                         context.getString(
503                             R.string.app_perms_24h_access_yest_background,
504                             summaryTimestamp.first
505                         )
506                     Utils.LAST_7D_SENSOR ->
507                         context.getString(
508                             R.string.app_perms_7d_access_background,
509                             summaryTimestamp.third,
510                             summaryTimestamp.first
511                         )
512                     Utils.NOT_IN_LAST_7D ->
513                         context.getString(R.string.permission_subtitle_background)
514                     else -> context.getString(R.string.permission_subtitle_background)
515                 }
516             PermSubtitle.MEDIA_ONLY ->
517                 when (lastAccessType) {
518                     Utils.LAST_24H_CONTENT_PROVIDER ->
519                         context.getString(R.string.app_perms_content_provider_24h_media_only)
520                     Utils.LAST_7D_CONTENT_PROVIDER ->
521                         context.getString(R.string.app_perms_content_provider_7d_media_only)
522                     Utils.LAST_24H_SENSOR_TODAY ->
523                         context.getString(
524                             R.string.app_perms_24h_access_media_only,
525                             summaryTimestamp.first
526                         )
527                     Utils.LAST_24H_SENSOR_YESTERDAY ->
528                         context.getString(
529                             R.string.app_perms_24h_access_yest_media_only,
530                             summaryTimestamp.first
531                         )
532                     Utils.LAST_7D_SENSOR ->
533                         context.getString(
534                             R.string.app_perms_7d_access_media_only,
535                             summaryTimestamp.third,
536                             summaryTimestamp.first
537                         )
538                     Utils.NOT_IN_LAST_7D ->
539                         context.getString(R.string.permission_subtitle_media_only)
540                     else -> context.getString(R.string.permission_subtitle_media_only)
541                 }
542             PermSubtitle.ALL_FILES ->
543                 when (lastAccessType) {
544                     Utils.LAST_24H_CONTENT_PROVIDER ->
545                         context.getString(R.string.app_perms_content_provider_24h_all_files)
546                     Utils.LAST_7D_CONTENT_PROVIDER ->
547                         context.getString(R.string.app_perms_content_provider_7d_all_files)
548                     Utils.LAST_24H_SENSOR_TODAY ->
549                         context.getString(
550                             R.string.app_perms_24h_access_all_files,
551                             summaryTimestamp.first
552                         )
553                     Utils.LAST_24H_SENSOR_YESTERDAY ->
554                         context.getString(
555                             R.string.app_perms_24h_access_yest_all_files,
556                             summaryTimestamp.first
557                         )
558                     Utils.LAST_7D_SENSOR ->
559                         context.getString(
560                             R.string.app_perms_7d_access_all_files,
561                             summaryTimestamp.third,
562                             summaryTimestamp.first
563                         )
564                     Utils.NOT_IN_LAST_7D ->
565                         context.getString(R.string.permission_subtitle_all_files)
566                     else -> context.getString(R.string.permission_subtitle_all_files)
567                 }
568             else ->
569                 // PermSubtitle.FOREGROUND_ONLY should fall into this as well
570                 when (lastAccessType) {
571                     Utils.LAST_24H_CONTENT_PROVIDER ->
572                         context.getString(R.string.app_perms_content_provider_24h)
573                     Utils.LAST_7D_CONTENT_PROVIDER ->
574                         context.getString(R.string.app_perms_content_provider_7d)
575                     Utils.LAST_24H_SENSOR_TODAY ->
576                         context.getString(R.string.app_perms_24h_access, summaryTimestamp.first)
577                     Utils.LAST_24H_SENSOR_YESTERDAY ->
578                         context.getString(
579                             R.string.app_perms_24h_access_yest,
580                             summaryTimestamp.first
581                         )
582                     Utils.LAST_7D_SENSOR ->
583                         context.getString(
584                             R.string.app_perms_7d_access,
585                             summaryTimestamp.third,
586                             summaryTimestamp.first
587                         )
588                     Utils.NOT_IN_LAST_7D -> ""
589                     else -> ""
590                 }
591         }
592     }
593 }
594 
595 /**
596  * Factory for an AppPermissionGroupsViewModel
597  *
598  * @param packageName The name of the package this viewModel is representing
599  * @param user The user of the package this viewModel is representing
600  */
601 class AppPermissionGroupsViewModelFactory(
602     private val packageName: String,
603     private val user: UserHandle,
604     private val sessionId: Long
605 ) : ViewModelProvider.Factory {
606 
createnull607     override fun <T : ViewModel> create(modelClass: Class<T>): T {
608         @Suppress("UNCHECKED_CAST")
609         return AppPermissionGroupsViewModel(packageName, user, sessionId) as T
610     }
611 }
612