diff options
author | 2025-02-02 22:11:20 -0800 | |
---|---|---|
committer | 2025-02-03 09:35:56 -0800 | |
commit | 314693e4b0099a955fa0cb974b575865bf7f19da (patch) | |
tree | c6dd7f3fc5991f92620c96200fcf9e6c8844f173 | |
parent | 4d9853a88e879df10068caaca5acd1f2f4258760 (diff) |
Remove Livedata implementation for permission timeline page
livedata_refactor_permission_timeline_enabled flag was enabled
in Nov mainline release, and the old impl can be removed now.
No functional change in this CL.
Bug: 354234946
Test: atest PermissionUsageDetailsViewModelTest PermissionUsageDetailsViewModelTest
FLAG: EXEMPT removing obsolete code
Relnote: code cleanup
Change-Id: I2555f963cfcc2ddfaf802f69064a2981717682ae
11 files changed, 360 insertions, 1259 deletions
diff --git a/PermissionController/lint-baseline.xml b/PermissionController/lint-baseline.xml index be77a0d18..b1570d0b7 100644 --- a/PermissionController/lint-baseline.xml +++ b/PermissionController/lint-baseline.xml @@ -1,16 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> - - <issue - id="NewApi" - message="Class requires API level 34 (current min is 31): `android.app.AppOpsManager.OnOpNotedListener`" - errorLine1=" AppOpsManager.OnOpNotedListener," - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightHistoricalPackageOpsLiveData.kt" - line="46" - column="5"/> - </issue> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" + variant="all" version="8.4.0-alpha01"> <issue id="NewApi" @@ -20,7 +10,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java" line="504" - column="31"/> + column="31" /> </issue> <issue @@ -31,7 +21,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java" line="505" - column="34"/> + column="34" /> </issue> <issue @@ -42,7 +32,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java" line="509" - column="41"/> + column="41" /> </issue> <issue @@ -53,7 +43,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java" line="513" - column="41"/> + column="41" /> </issue> <issue @@ -64,7 +54,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java" line="515" - column="59"/> + column="59" /> </issue> <issue @@ -75,7 +65,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java" line="518" - column="42"/> + column="42" /> </issue> <issue @@ -86,7 +76,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java" line="519" - column="42"/> + column="42" /> </issue> <issue @@ -97,7 +87,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java" line="521" - column="18"/> + column="18" /> </issue> <issue @@ -108,7 +98,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java" line="522" - column="29"/> + column="29" /> </issue> <issue @@ -119,7 +109,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java" line="709" - column="31"/> + column="31" /> </issue> <issue @@ -130,7 +120,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java" line="710" - column="34"/> + column="34" /> </issue> <issue @@ -141,7 +131,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java" line="714" - column="41"/> + column="41" /> </issue> <issue @@ -152,7 +142,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java" line="718" - column="41"/> + column="41" /> </issue> <issue @@ -163,7 +153,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java" line="720" - column="59"/> + column="59" /> </issue> <issue @@ -174,7 +164,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java" line="722" - column="67"/> + column="67" /> </issue> <issue @@ -185,7 +175,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java" line="722" - column="42"/> + column="42" /> </issue> <issue @@ -196,7 +186,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java" line="724" - column="18"/> + column="18" /> </issue> <issue @@ -207,7 +197,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java" line="725" - column="29"/> + column="29" /> </issue> <issue @@ -218,7 +208,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionsFragment.java" line="159" - column="35"/> + column="35" /> </issue> <issue @@ -229,7 +219,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionsFragment.java" line="367" - column="35"/> + column="35" /> </issue> <issue @@ -240,7 +230,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java" line="463" - column="31"/> + column="31" /> </issue> <issue @@ -251,7 +241,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java" line="464" - column="34"/> + column="34" /> </issue> <issue @@ -262,7 +252,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java" line="468" - column="41"/> + column="41" /> </issue> <issue @@ -273,7 +263,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java" line="472" - column="41"/> + column="41" /> </issue> <issue @@ -284,7 +274,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java" line="474" - column="59"/> + column="59" /> </issue> <issue @@ -295,7 +285,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java" line="476" - column="67"/> + column="67" /> </issue> <issue @@ -306,7 +296,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java" line="476" - column="42"/> + column="42" /> </issue> <issue @@ -317,7 +307,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java" line="478" - column="18"/> + column="18" /> </issue> <issue @@ -328,7 +318,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java" line="479" - column="29"/> + column="29" /> </issue> <issue @@ -339,7 +329,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt" line="1059" - column="45"/> + column="45" /> </issue> <issue @@ -350,7 +340,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt" line="48" - column="74"/> + column="74" /> </issue> <issue @@ -361,7 +351,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt" line="51" - column="44"/> + column="44" /> </issue> <issue @@ -372,7 +362,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardAnimator.kt" line="47" - column="37"/> + column="37" /> </issue> <issue @@ -383,7 +373,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt" line="653" - column="29"/> + column="29" /> </issue> <issue @@ -394,7 +384,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt" line="662" - column="18"/> + column="18" /> </issue> <issue @@ -405,7 +395,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt" line="1689" - column="48"/> + column="48" /> </issue> <issue @@ -416,7 +406,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt" line="140" - column="72"/> + column="72" /> </issue> <issue @@ -427,7 +417,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt" line="140" - column="62"/> + column="62" /> </issue> <issue @@ -438,7 +428,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt" line="288" - column="19"/> + column="19" /> </issue> <issue @@ -449,7 +439,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt" line="291" - column="48"/> + column="48" /> </issue> <issue @@ -460,7 +450,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt" line="291" - column="30"/> + column="30" /> </issue> <issue @@ -471,7 +461,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt" line="292" - column="30"/> + column="30" /> </issue> <issue @@ -482,7 +472,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt" line="292" - column="39"/> + column="39" /> </issue> <issue @@ -493,7 +483,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt" line="298" - column="5"/> + column="5" /> </issue> <issue @@ -504,7 +494,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt" line="298" - column="12"/> + column="12" /> </issue> <issue @@ -515,7 +505,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt" line="301" - column="34"/> + column="34" /> </issue> <issue @@ -526,7 +516,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt" line="308" - column="13"/> + column="13" /> </issue> <issue @@ -537,7 +527,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardAnimator.kt" line="110" - column="21"/> + column="21" /> </issue> <issue @@ -548,7 +538,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardAnimator.kt" line="110" - column="46"/> + column="46" /> </issue> <issue @@ -559,7 +549,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionAppsFragment.java" line="114" - column="35"/> + column="35" /> </issue> <issue @@ -570,7 +560,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionAppsFragment.java" line="365" - column="35"/> + column="35" /> </issue> <issue @@ -581,7 +571,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt" line="100" - column="43"/> + column="43" /> </issue> <issue @@ -592,7 +582,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java" line="521" - column="43"/> + column="43" /> </issue> <issue @@ -603,7 +593,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java" line="521" - column="75"/> + column="75" /> </issue> <issue @@ -614,7 +604,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java" line="522" - column="48"/> + column="48" /> </issue> <issue @@ -625,7 +615,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java" line="522" - column="24"/> + column="24" /> </issue> <issue @@ -636,7 +626,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java" line="85" - column="42"/> + column="42" /> </issue> <issue @@ -647,7 +637,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java" line="97" - column="34"/> + column="34" /> </issue> <issue @@ -658,7 +648,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java" line="141" - column="73"/> + column="73" /> </issue> <issue @@ -669,7 +659,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java" line="146" - column="34"/> + column="34" /> </issue> <issue @@ -680,7 +670,7 @@ <location file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java" line="152" - column="29"/> + column="29" /> </issue> </issues>
\ No newline at end of file diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightHistoricalPackageOpsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightHistoricalPackageOpsLiveData.kt deleted file mode 100644 index a3fc40232..000000000 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightHistoricalPackageOpsLiveData.kt +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.permissioncontroller.permission.data.v31 - -import android.app.AppOpsManager -import android.app.AppOpsManager.HISTORY_FLAG_DISCRETE -import android.app.AppOpsManager.HISTORY_FLAG_GET_ATTRIBUTION_CHAINS -import android.app.AppOpsManager.HistoricalOps -import android.app.AppOpsManager.HistoricalOpsRequest -import android.app.AppOpsManager.OP_FLAG_SELF -import android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED -import android.app.Application -import android.os.UserHandle -import android.os.UserManager -import com.android.modules.utils.build.SdkLevel -import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData -import com.android.permissioncontroller.permission.model.livedatatypes.v31.LightHistoricalPackageOps -import java.util.concurrent.TimeUnit -import kotlin.coroutines.suspendCoroutine -import kotlinx.coroutines.Job - -/** - * LiveData class tracking [LightHistoricalPackageOps] for all packages on the device and for the - * provided app ops. - * - * App ops data is retrieved from [AppOpsManager] and is updated whenever app ops data changes are - * heard. - */ -class AllLightHistoricalPackageOpsLiveData(app: Application, val opNames: Set<String>) : - SmartAsyncMediatorLiveData<Map<Pair<String, UserHandle>, LightHistoricalPackageOps>>(), - AppOpsManager.OnOpActiveChangedListener, - AppOpsManager.OnOpNotedListener, - AppOpsManager.OnOpChangedListener { - - private val appOpsManager = app.getSystemService(AppOpsManager::class.java)!! - private val userManager = app.getSystemService(UserManager::class.java)!! - - override fun onActive() { - super.onActive() - - opNames.forEach { opName -> - // TODO(b/262035952): We watch each active op individually as startWatchingActive only - // registers the callback if all ops are valid. Fix this behavior so if one op is - // invalid it doesn't affect the other ops. - try { - appOpsManager.startWatchingActive(arrayOf(opName), { it.run() }, this) - } catch (ignored: IllegalArgumentException) { - // Older builds may not support all requested app ops. - } - - try { - appOpsManager.startWatchingMode(opName, /* all packages */ null, this) - } catch (ignored: IllegalArgumentException) { - // Older builds may not support all requested app ops. - } - - if (SdkLevel.isAtLeastU()) { - try { - appOpsManager.startWatchingNoted(arrayOf(opName), this) - } catch (ignored: IllegalArgumentException) { - // Older builds may not support all requested app ops. - } - } - } - } - - override fun onInactive() { - super.onInactive() - - appOpsManager.stopWatchingActive(this) - appOpsManager.stopWatchingMode(this) - } - - override suspend fun loadDataAndPostValue(job: Job) { - if (job.isCancelled) { - return - } - - val allLightHistoricalPackageOps = - mutableMapOf<Pair<String, UserHandle>, LightHistoricalPackageOps>() - - val endTimeMillis = System.currentTimeMillis() - val beginTimeMillis = endTimeMillis - TimeUnit.DAYS.toMillis(7) - - val allProfilesInCurrentUser = userManager.userProfiles - - val request = - HistoricalOpsRequest.Builder(beginTimeMillis, endTimeMillis) - .setFlags(OP_FLAG_SELF or OP_FLAG_TRUSTED_PROXIED) - .setHistoryFlags(HISTORY_FLAG_DISCRETE or HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) - .build() - - val historicalOps = suspendCoroutine { - appOpsManager.getHistoricalOps(request, { it.run() }) { ops: HistoricalOps -> - it.resumeWith(Result.success(ops)) - } - } - - for (i in 0 until historicalOps.uidCount) { - val historicalUidOps = historicalOps.getUidOpsAt(i) - val userHandle = UserHandle.getUserHandleForUid(historicalUidOps.uid) - if (userHandle !in allProfilesInCurrentUser) { - continue - } - for (j in 0 until historicalUidOps.packageCount) { - val historicalPackageOps = historicalUidOps.getPackageOpsAt(j) - allLightHistoricalPackageOps[Pair(historicalPackageOps.packageName, userHandle)] = - LightHistoricalPackageOps(historicalPackageOps, userHandle, opNames) - } - } - - postValue(allLightHistoricalPackageOps) - } - - override fun onOpChanged(op: String?, packageName: String?) { - update() - } - - override fun onOpActiveChanged(op: String, uid: Int, packageName: String, active: Boolean) { - update() - } - - override fun onOpNoted( - code: String, - uid: Int, - packageName: String, - attributionTag: String?, - flags: Int, - result: Int - ) { - update() - } -} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt index 8edd39913..2ecf40ce2 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt @@ -35,7 +35,6 @@ import com.android.permissioncontroller.R import com.android.permissioncontroller.auto.AutoSettingsFrameFragment import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity import com.android.permissioncontroller.permission.ui.auto.AutoDividerPreference -import com.android.permissioncontroller.permission.ui.model.v31.BasePermissionUsageDetailsViewModel import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.AppPermissionAccessUiInfo import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel @@ -80,7 +79,7 @@ class AutoPermissionUsageDetailsFragment : AutoSettingsFrameFragment() { private val SESSION_ID_KEY = (AutoPermissionUsageFragment::class.java.name + KEY_SESSION_ID) - private lateinit var usageViewModel: BasePermissionUsageDetailsViewModel + private lateinit var usageViewModel: PermissionUsageDetailsViewModel private lateinit var filterGroup: String private var showSystem = false @@ -119,11 +118,10 @@ class AutoPermissionUsageDetailsFragment : AutoSettingsFrameFragment() { val factory = PermissionUsageDetailsViewModel.PermissionUsageDetailsViewModelFactory( PermissionControllerApplication.get(), - this, filterGroup, ) usageViewModel = - ViewModelProvider(this, factory)[BasePermissionUsageDetailsViewModel::class.java] + ViewModelProvider(this, factory)[PermissionUsageDetailsViewModel::class.java] usageViewModel.getPermissionUsagesDetailsInfoUiLiveData().observe(this, this::updateUI) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java index 3a904c466..9078f1a0a 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java @@ -54,7 +54,7 @@ import com.android.permissioncontroller.PermissionControllerApplication; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity; import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader; -import com.android.permissioncontroller.permission.ui.model.v31.BasePermissionUsageDetailsViewModel; +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel; import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.AppPermissionAccessUiInfo; import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState; import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsViewModelFactory; @@ -89,7 +89,7 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { private MenuItem mShow7DaysDataMenu; private MenuItem mShow24HoursDataMenu; - private BasePermissionUsageDetailsViewModel mViewModel; + private PermissionUsageDetailsViewModel mViewModel; private long mSessionId; @@ -103,9 +103,9 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { } PermissionUsageDetailsViewModelFactory factory = new PermissionUsageDetailsViewModelFactory( - PermissionControllerApplication.get(), this, mPermissionGroup); + PermissionControllerApplication.get(), mPermissionGroup); mViewModel = - new ViewModelProvider(this, factory).get(BasePermissionUsageDetailsViewModel.class); + new ViewModelProvider(this, factory).get(PermissionUsageDetailsViewModel.class); if (savedInstanceState != null) { mSessionId = savedInstanceState.getLong(SESSION_ID_KEY); diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/BasePermissionUsageDetailsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/BasePermissionUsageDetailsViewModel.kt deleted file mode 100644 index 64c5c6927..000000000 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/BasePermissionUsageDetailsViewModel.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.permissioncontroller.permission.ui.model.v31 - -import android.app.Application -import android.graphics.drawable.Drawable -import android.os.UserHandle -import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.LiveData -import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.ui.handheld.v31.getDurationUsedStr -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.CLUSTER_SPACING_MINUTES -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState -import com.android.permissioncontroller.permission.utils.KotlinUtils -import java.util.concurrent.TimeUnit - -abstract class BasePermissionUsageDetailsViewModel(val app: Application) : AndroidViewModel(app) { - abstract fun getPermissionUsagesDetailsInfoUiLiveData(): LiveData<PermissionUsageDetailsUiState> - - abstract fun getShowSystem(): Boolean - - abstract val showSystemLiveData: LiveData<Boolean> - - abstract fun getShow7Days(): Boolean - - abstract fun updateShowSystemAppsToggle(showSystem: Boolean) - - abstract fun updateShow7DaysToggle(show7Days: Boolean) - - private val packageIconCache: MutableMap<Pair<String, UserHandle>, Drawable> = mutableMapOf() - private val packageLabelCache: MutableMap<String, String> = mutableMapOf() - - /** - * Returns the label for the provided package name, by first searching the cache otherwise - * retrieving it from the app's [android.content.pm.ApplicationInfo]. - */ - fun getPackageLabel(packageName: String, user: UserHandle): String { - if (packageLabelCache.containsKey(packageName)) { - return requireNotNull(packageLabelCache[packageName]) - } - val packageLabel = getPackageLabel(app, packageName, user) - packageLabelCache[packageName] = packageLabel - return packageLabel - } - - open fun getPackageLabel(app: Application, packageName: String, user: UserHandle): String { - return KotlinUtils.getPackageLabel(app, packageName, user) - } - - /** - * Returns the icon for the provided package name and user, by first searching the cache - * otherwise retrieving it from the app's [android.content.pm.ApplicationInfo]. - */ - fun getBadgedPackageIcon(packageName: String, userHandle: UserHandle): Drawable? { - val packageNameWithUser: Pair<String, UserHandle> = Pair(packageName, userHandle) - if (packageIconCache.containsKey(packageNameWithUser)) { - return requireNotNull(packageIconCache[packageNameWithUser]) - } - val packageIcon = getBadgedPackageIcon(app, packageName, userHandle) - if (packageIcon != null) packageIconCache[packageNameWithUser] = packageIcon - - return packageIcon - } - - open fun getBadgedPackageIcon( - app: Application, - packageName: String, - user: UserHandle - ): Drawable? { - return KotlinUtils.getBadgedPackageIcon(app, packageName, user) - } - - fun getDurationSummary(durationMs: Long): String? { - // Only show the duration summary if it is at least (CLUSTER_SPACING_MINUTES + 1) minutes. - // Displaying a time that is shorter than the cluster granularity - // (CLUSTER_SPACING_MINUTES) will not convey useful information. - if (durationMs >= TimeUnit.MINUTES.toMillis(CLUSTER_SPACING_MINUTES + 1)) { - return getDurationUsedStr(app, durationMs) - } - return null - } - - fun buildUsageSummary( - subAttributionLabel: String?, - proxyPackageLabel: String?, - durationSummary: String? - ): String? { - val subTextStrings: MutableList<String> = mutableListOf() - subAttributionLabel?.let { subTextStrings.add(subAttributionLabel) } - proxyPackageLabel?.let { subTextStrings.add(it) } - durationSummary?.let { subTextStrings.add(it) } - return when (subTextStrings.size) { - 3 -> - app.getString( - R.string.history_preference_subtext_3, - subTextStrings[0], - subTextStrings[1], - subTextStrings[2] - ) - 2 -> - app.getString( - R.string.history_preference_subtext_2, - subTextStrings[0], - subTextStrings[1] - ) - 1 -> subTextStrings[0] - else -> null - } - } -} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt index 497e5cc35..ad21ab220 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,677 +13,288 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@file:Suppress("DEPRECATION") package com.android.permissioncontroller.permission.ui.model.v31 import android.Manifest import android.app.AppOpsManager +import android.app.AppOpsManager.OPSTR_EMERGENCY_LOCATION import android.app.AppOpsManager.OPSTR_PHONE_CALL_CAMERA import android.app.AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE import android.app.Application -import android.app.role.RoleManager import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager -import android.content.res.Resources import android.graphics.drawable.Drawable import android.location.LocationManager import android.os.Build -import android.os.Bundle import android.os.UserHandle -import android.os.UserManager import android.permission.flags.Flags -import android.util.Log import androidx.annotation.RequiresApi -import androidx.lifecycle.AbstractSavedStateViewModelFactory +import androidx.annotation.VisibleForTesting +import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel -import androidx.savedstate.SavedStateRegistryOwner +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.application +import androidx.lifecycle.asLiveData +import androidx.lifecycle.createSavedStateHandle +import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.CreationExtras import com.android.modules.utils.build.SdkLevel import com.android.permissioncontroller.DeviceUtils import com.android.permissioncontroller.R +import com.android.permissioncontroller.appops.data.repository.v31.AppOpRepository import com.android.permissioncontroller.permission.compat.IntentCompat -import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData -import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData -import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData -import com.android.permissioncontroller.permission.data.get -import com.android.permissioncontroller.permission.data.v31.AllLightHistoricalPackageOpsLiveData -import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo -import com.android.permissioncontroller.permission.model.livedatatypes.v31.AppPermissionId -import com.android.permissioncontroller.permission.model.livedatatypes.v31.LightHistoricalPackageOps -import com.android.permissioncontroller.permission.model.livedatatypes.v31.LightHistoricalPackageOps.AppPermissionDiscreteAccesses -import com.android.permissioncontroller.permission.model.livedatatypes.v31.LightHistoricalPackageOps.AttributedAppPermissionDiscreteAccesses -import com.android.permissioncontroller.permission.model.livedatatypes.v31.LightHistoricalPackageOps.Companion.NO_ATTRIBUTION_TAG -import com.android.permissioncontroller.permission.model.livedatatypes.v31.LightHistoricalPackageOps.DiscreteAccess +import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository +import com.android.permissioncontroller.permission.domain.model.v31.PermissionTimelineUsageModel +import com.android.permissioncontroller.permission.domain.model.v31.PermissionTimelineUsageModelWrapper +import com.android.permissioncontroller.permission.domain.usecase.v31.GetPermissionGroupUsageDetailsUseCase import com.android.permissioncontroller.permission.ui.handheld.v31.getDurationUsedStr -import com.android.permissioncontroller.permission.ui.handheld.v31.shouldShowSubattributionInPermissionsDashboard import com.android.permissioncontroller.permission.utils.PermissionMapping -import com.android.permissioncontroller.permission.utils.Utils -import com.android.permissioncontroller.permission.utils.v31.SubattributionUtils +import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository +import com.android.permissioncontroller.role.data.repository.v31.RoleRepository +import com.android.permissioncontroller.user.data.repository.v31.UserRepository import java.time.Instant import java.util.Objects import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit.DAYS +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.stateIn -/** [ViewModel] for the Permission Usage Details page. */ -@RequiresApi(Build.VERSION_CODES.S) class PermissionUsageDetailsViewModel( - private val application: Application, + app: Application, + private val getPermissionUsageDetailsUseCase: GetPermissionGroupUsageDetailsUseCase, private val state: SavedStateHandle, private val permissionGroup: String, -) : BasePermissionUsageDetailsViewModel(application) { - - val allLightHistoricalPackageOpsLiveData = - AllLightHistoricalPackageOpsLiveData(application, opNames) - private val appPermGroupUiInfoLiveDataList = - mutableMapOf<AppPermissionId, AppPermGroupUiInfoLiveData>() - private val lightPackageInfoLiveDataMap = - mutableMapOf<Pair<String, UserHandle>, LightPackageInfoLiveData>() - - override val showSystemLiveData = state.getLiveData(SHOULD_SHOW_SYSTEM_KEY, false) - val show7DaysLiveData = state.getLiveData(SHOULD_SHOW_7_DAYS_KEY, false) - - private val roleManager = - Utils.getSystemServiceSafe(application.applicationContext, RoleManager::class.java) - private val userManager = - Utils.getSystemServiceSafe(application.applicationContext, UserManager::class.java) + scope: CoroutineScope? = null, + private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default, + private val packageRepository: PackageRepository = PackageRepository.getInstance(app), +) : AndroidViewModel(app) { + private val coroutineScope = scope ?: viewModelScope + private val context = app + + private val packageIconCache: MutableMap<Pair<String, UserHandle>, Drawable> = mutableMapOf() + private val packageLabelCache: MutableMap<String, String> = mutableMapOf() + + private val showSystemFlow = MutableStateFlow(state[SHOULD_SHOW_SYSTEM_KEY] ?: false) + private val show7DaysFlow = MutableStateFlow(state[SHOULD_SHOW_7_DAYS_KEY] ?: false) + + private val permissionTimelineUsagesFlow: + StateFlow<PermissionTimelineUsageModelWrapper> by lazy { + getPermissionUsageDetailsUseCase(coroutineScope) + .flowOn(defaultDispatcher) + .stateIn( + coroutineScope, + SharingStarted.WhileSubscribed(5000), + PermissionTimelineUsageModelWrapper.Loading, + ) + } - /** Updates whether system app permissions usage should be displayed in the UI. */ - override fun updateShowSystemAppsToggle(showSystem: Boolean) { - if (showSystem != state[SHOULD_SHOW_SYSTEM_KEY]) { - state[SHOULD_SHOW_SYSTEM_KEY] = showSystem - } + @VisibleForTesting + val permissionUsageDetailsUiStateFlow: Flow<PermissionUsageDetailsUiState> by lazy { + combine(permissionTimelineUsagesFlow, showSystemFlow, show7DaysFlow) { + permissionTimelineUsages, + showSystem, + show7Days -> + permissionTimelineUsages.buildPermissionUsageDetailsUiInfo(showSystem, show7Days) + } + .flowOn(defaultDispatcher) } - /** Updates whether 7 days usage or 1 day usage should be displayed in the UI. */ - override fun updateShow7DaysToggle(show7Days: Boolean) { - if (show7Days != state[SHOULD_SHOW_7_DAYS_KEY]) { - state[SHOULD_SHOW_7_DAYS_KEY] = show7Days - } + fun getPermissionUsagesDetailsInfoUiLiveData(): LiveData<PermissionUsageDetailsUiState> { + return permissionUsageDetailsUiStateFlow.asLiveData( + context = coroutineScope.coroutineContext + ) } - /** Creates a [PermissionUsageDetailsUiState] containing all information to render the UI. */ - fun buildPermissionUsageDetailsUiInfo(): PermissionUsageDetailsUiState { - val showSystem: Boolean = state[SHOULD_SHOW_SYSTEM_KEY] ?: false - val show7Days: Boolean = state[SHOULD_SHOW_7_DAYS_KEY] ?: false - val showPermissionUsagesDuration = - if (show7Days && DeviceUtils.isHandheld()) { - TIME_7_DAYS_DURATION - } else { - TIME_24_HOURS_DURATION - } + private fun PermissionTimelineUsageModelWrapper.buildPermissionUsageDetailsUiInfo( + showSystem: Boolean, + show7Days: Boolean, + ): PermissionUsageDetailsUiState { + if (this is PermissionTimelineUsageModelWrapper.Loading) { + return PermissionUsageDetailsUiState.Loading + } + val timelineUsageModels = + (this as PermissionTimelineUsageModelWrapper.Success).timelineUsageModels val startTime = - (System.currentTimeMillis() - showPermissionUsagesDuration).coerceAtLeast( + (System.currentTimeMillis() - getUsageDuration(show7Days)).coerceAtLeast( Instant.EPOCH.toEpochMilli() ) + val permissionTimelineUsageModels = + timelineUsageModels.filter { it.accessEndMillis > startTime } + val containsSystemUsages = permissionTimelineUsageModels.any { !it.isUserSensitive } + val result = + permissionTimelineUsageModels + .filter { showSystem || it.isUserSensitive } + .map { clusterOps -> + val durationSummaryLabel = + if (clusterOps.durationMillis > 0) { + getDurationSummary(clusterOps.durationMillis) + } else { + null + } + val proxyLabel = getProxyPackageLabel(clusterOps) + val isEmergencyLocationAccess = + isLocationByPassEnabled() && + clusterOps.opNames.any { it == OPSTR_EMERGENCY_LOCATION } + val subAttributionLabel = + if (isEmergencyLocationAccess) { + emergencyLocationAttributionLabel + } else { + clusterOps.attributionLabel + } + val showingSubAttribution = !subAttributionLabel.isNullOrEmpty() + val summary = + buildUsageSummary(subAttributionLabel, proxyLabel, durationSummaryLabel) + PermissionUsageDetailsViewModel.AppPermissionAccessUiInfo( + UserHandle.of(clusterOps.userId), + clusterOps.packageName, + getPackageLabel(clusterOps.packageName, UserHandle.of(clusterOps.userId)), + permissionGroup, + clusterOps.accessStartMillis, + clusterOps.accessEndMillis, + summary, + showingSubAttribution, + clusterOps.attributionTags ?: emptySet(), + getBadgedPackageIcon( + clusterOps.packageName, + UserHandle.of(clusterOps.userId), + ), + isEmergencyLocationAccess, + ) + } + .sortedBy { -1 * it.accessStartTime } return PermissionUsageDetailsUiState.Success( - buildAppPermissionAccessUiInfoList( - allLightHistoricalPackageOpsLiveData, - startTime, - showSystem - ), - containsSystemAppUsages(allLightHistoricalPackageOpsLiveData, startTime), + result, + containsSystemUsages, showSystem, - show7Days + show7Days, ) } - /** - * Returns whether the "show/hide system" toggle should be displayed in the UI for the provided - * [AllLightHistoricalPackageOpsLiveData]. - */ - private fun containsSystemAppUsages( - allLightHistoricalPackageOpsLiveData: AllLightHistoricalPackageOpsLiveData, - startTime: Long - ): Boolean { - return allLightHistoricalPackageOpsLiveData - .getLightHistoricalPackageOps() - ?.flatMap { - it.appPermissionDiscreteAccesses - .map { it.withLabel() } - .filterOutExemptAppPermissions(true) - .filterAccessesLaterThan(startTime) - } - ?.any { isAppPermissionSystem(it.appPermissionId) } ?: false + private val emergencyLocationAttributionLabel: String by lazy { + context.getString(R.string.privacy_dashboard_emergency_location_enforced_attribution_label) } - private fun isPermissionRequestedByApp(appPermissionId: AppPermissionId): Boolean { - val appRequestedPermissions = - lightPackageInfoLiveDataMap[ - Pair(appPermissionId.packageName, appPermissionId.userHandle)] - ?.value - ?.requestedPermissions ?: listOf() - - if ( - appPermissionId.permissionGroup == Manifest.permission_group.LOCATION && - appRequestedPermissions.contains(Manifest.permission.LOCATION_BYPASS) - ) { - return true - } + fun getShowSystem(): Boolean = showSystemFlow.value - return appRequestedPermissions.any { - PermissionMapping.getGroupOfPlatformPermission(it) == appPermissionId.permissionGroup - } - } + val showSystemLiveData = showSystemFlow.asLiveData(context = coroutineScope.coroutineContext) - private fun isAppPermissionSystem(appPermissionId: AppPermissionId): Boolean { - val appPermGroupUiInfo = appPermGroupUiInfoLiveDataList[appPermissionId]?.value - - if (appPermGroupUiInfo != null) { - return appPermGroupUiInfo.isSystem - } else - // The AppPermGroupUiInfo may be null if it has either not loaded yet or if the app has not - // requested any permissions from the permission group in question. - // The Telecom doesn't request microphone or camera permissions. However, telecom app may - // use these permissions and they are considered system app permissions, so we return true - // even if the AppPermGroupUiInfo is unavailable. - if ( - appPermissionId.packageName == TELECOM_PACKAGE && - (appPermissionId.permissionGroup == Manifest.permission_group.CAMERA || - appPermissionId.permissionGroup == Manifest.permission_group.MICROPHONE) - ) { - return true + fun getShow7Days(): Boolean = show7DaysFlow.value + + private fun getUsageDuration(show7Days: Boolean): Long { + return if (show7Days && DeviceUtils.isHandheld()) { + TIME_7_DAYS_DURATION + } else { + TIME_24_HOURS_DURATION } - return false } - /** - * Extracts access data from [AllLightHistoricalPackageOpsLiveData] and composes - * [AppPermissionAccessUiInfo]s to be displayed in the UI. - */ - private fun buildAppPermissionAccessUiInfoList( - allLightHistoricalPackageOpsLiveData: AllLightHistoricalPackageOpsLiveData, - startTime: Long, - showSystem: Boolean - ): List<AppPermissionAccessUiInfo> { - return allLightHistoricalPackageOpsLiveData - .getLightHistoricalPackageOps() - ?.filter { Utils.shouldShowInSettings(it.userHandle, userManager) } - ?.flatMap { it.clusterAccesses(startTime, showSystem) } - ?.map { it.buildAppPermissionAccessUiInfo() } - ?.sortedBy { -it.accessStartTime } ?: listOf() - } + private fun getProxyPackageLabel(accessCluster: PermissionTimelineUsageModel): String? = + accessCluster.proxyPackageName?.let { proxyPackageName -> + if (accessCluster.proxyUserId != null) { + getPackageLabel(proxyPackageName, UserHandle.of(accessCluster.proxyUserId)) + } else null + } - private fun LightHistoricalPackageOps.clusterAccesses( - startTime: Long, - showSystem: Boolean - ): List<AppPermissionDiscreteAccessCluster> { - return if (!shouldShowSubAttributionForApp(getLightPackageInfo(packageName, userHandle))) - this.clusterAccessesWithoutAttribution(startTime, showSystem) - else { - this.clusterAccessesWithAttribution(startTime, showSystem) + fun updateShowSystemAppsToggle(showSystem: Boolean) { + if (showSystem != state[SHOULD_SHOW_SYSTEM_KEY]) { + state[SHOULD_SHOW_SYSTEM_KEY] = showSystem } + showSystemFlow.compareAndSet(!showSystem, showSystem) } - /** - * Clusters accesses that are close enough together in time such that they can be displayed as a - * single access to the user. - * - * Accesses are clustered taking into account any app subattribution, so each cluster will - * pertain a particular attribution label. - */ - private fun LightHistoricalPackageOps.clusterAccessesWithAttribution( - startTime: Long, - showSystem: Boolean - ): List<AppPermissionDiscreteAccessCluster> = - this.attributedAppPermissionDiscreteAccesses - .flatMap { it.groupAccessesByLabel(getLightPackageInfo(packageName, userHandle)) } - .filterOutExemptAppPermissions(showSystem) - .filterAccessesLaterThan(startTime) - .flatMap { createAccessClusters(it) } - - /** - * Clusters accesses that are close enough together in time such that they can be displayed as a - * single access to the user. - * - * Accesses are clustered disregarding any app subattribution. - */ - private fun LightHistoricalPackageOps.clusterAccessesWithoutAttribution( - startTime: Long, - showSystem: Boolean - ): List<AppPermissionDiscreteAccessCluster> = - this.appPermissionDiscreteAccesses - .map { it.withLabel() } - .filterOutExemptAppPermissions(showSystem) - .filterAccessesLaterThan(startTime) - .flatMap { createAccessClusters(it) } - - /** Filters out accesses earlier than the provided start time. */ - private fun List<AppPermissionDiscreteAccessesWithLabel>.filterAccessesLaterThan( - startTime: Long, - ): List<AppPermissionDiscreteAccessesWithLabel> = - this.mapNotNull { - val updatedDiscreteAccesses = - it.discreteAccesses.filter { access -> access.accessTimeMs > startTime } - if (updatedDiscreteAccesses.isEmpty()) null - else - AppPermissionDiscreteAccessesWithLabel( - it.appPermissionId, - it.attributionLabel, - it.attributionTags, - updatedDiscreteAccesses - ) + fun updateShow7DaysToggle(show7Days: Boolean) { + if (show7Days != state[SHOULD_SHOW_7_DAYS_KEY]) { + state[SHOULD_SHOW_7_DAYS_KEY] = show7Days } - - /** Filters out data for apps and permissions that don't need to be displayed in the UI. */ - private fun List<AppPermissionDiscreteAccessesWithLabel>.filterOutExemptAppPermissions( - showSystem: Boolean - ): List<AppPermissionDiscreteAccessesWithLabel> { - val exemptedPackages = Utils.getExemptedPackages(roleManager) - return filter { !exemptedPackages.contains(it.appPermissionId.packageName) } - .filter { it.appPermissionId.permissionGroup == permissionGroup } - .filter { isPermissionRequestedByApp(it.appPermissionId) } - .filter { showSystem || !isAppPermissionSystem(it.appPermissionId) } + show7DaysFlow.compareAndSet(!show7Days, show7Days) } /** - * Converts the provided [AppPermissionDiscreteAccesses] to a - * [AppPermissionDiscreteAccessesWithLabel] by adding a label. + * Returns the label for the provided package name, by first searching the cache otherwise + * retrieving it from the app's [android.content.pm.ApplicationInfo]. */ - private fun AppPermissionDiscreteAccesses.withLabel(): AppPermissionDiscreteAccessesWithLabel = - AppPermissionDiscreteAccessesWithLabel( - this.appPermissionId, - Resources.ID_NULL, - attributionTags = emptyList(), - this.discreteAccesses - ) - - /** Groups tag-attributed accesses for the provided app and permission by attribution label. */ - private fun AttributedAppPermissionDiscreteAccesses.groupAccessesByLabel( - lightPackageInfo: LightPackageInfo? - ): List<AppPermissionDiscreteAccessesWithLabel> { - if (lightPackageInfo == null) return emptyList() - - val appPermissionId = this.appPermissionId - val labelsToDiscreteAccesses = mutableMapOf<Int, MutableList<DiscreteAccess>>() - val labelsToTags = mutableMapOf<Int, MutableList<String>>() - - val appPermissionDiscreteAccessWithLabels = - mutableListOf<AppPermissionDiscreteAccessesWithLabel>() - - for ((tag, discreteAccesses) in this.attributedDiscreteAccesses) { - val label: Int = - if (tag == NO_ATTRIBUTION_TAG) Resources.ID_NULL - else lightPackageInfo.attributionTagsToLabels[tag] ?: Resources.ID_NULL - - if (!labelsToDiscreteAccesses.containsKey(label)) { - labelsToDiscreteAccesses[label] = mutableListOf() - } - labelsToDiscreteAccesses[label]?.addAll(discreteAccesses) - - if (!labelsToTags.containsKey(label)) { - labelsToTags[label] = mutableListOf() - } - labelsToTags[label]?.add(tag) - } - - for ((label, discreteAccesses) in labelsToDiscreteAccesses.entries) { - val tags = labelsToTags[label]?.toList() ?: listOf() - - appPermissionDiscreteAccessWithLabels.add( - AppPermissionDiscreteAccessesWithLabel( - appPermissionId, - label, - tags, - discreteAccesses.sortedBy { -1 * it.accessTimeMs } - ) - ) + fun getPackageLabel(packageName: String, user: UserHandle): String { + if (packageLabelCache.containsKey(packageName)) { + return requireNotNull(packageLabelCache[packageName]) } - - return appPermissionDiscreteAccessWithLabels + val packageLabel = packageRepository.getPackageLabel(packageName, user) + packageLabelCache[packageName] = packageLabel + return packageLabel } /** - * Clusters [DiscreteAccess]es represented by a [AppPermissionDiscreteAccessesWithLabel] into - * smaller groups to form a list of [AppPermissionDiscreteAccessCluster] instances. - * - * [DiscreteAccess]es which have accesses sufficiently close together in time will be places in - * the same cluster. + * Returns the icon for the provided package name and user, by first searching the cache + * otherwise retrieving it from the app's [android.content.pm.ApplicationInfo]. */ - private fun createAccessClusters( - appPermAccesses: AppPermissionDiscreteAccessesWithLabel, - ): List<AppPermissionDiscreteAccessCluster> { - val clusters = mutableListOf<AppPermissionDiscreteAccessCluster>() - val currentDiscreteAccesses = mutableListOf<DiscreteAccess>() - // Iterate entries in asc order based on access timestamp. - for (index in appPermAccesses.discreteAccesses.size - 1 downTo 0) { - val discreteAccess = appPermAccesses.discreteAccesses[index] - if (currentDiscreteAccesses.isEmpty()) { - currentDiscreteAccesses.add(discreteAccess) - } else if (!canAccessBeAddedToCluster(discreteAccess, currentDiscreteAccesses)) { - clusters.add( - AppPermissionDiscreteAccessCluster( - appPermAccesses.appPermissionId, - appPermAccesses.attributionLabel, - appPermAccesses.attributionTags, - currentDiscreteAccesses.toMutableList(), - if (isOpClusteredByItself(discreteAccess.opName)) discreteAccess.opName - else null - ) - ) - currentDiscreteAccesses.clear() - currentDiscreteAccesses.add(discreteAccess) - } else { - currentDiscreteAccesses.add(discreteAccess) - } + fun getBadgedPackageIcon(packageName: String, userHandle: UserHandle): Drawable? { + val packageNameWithUser: Pair<String, UserHandle> = Pair(packageName, userHandle) + if (packageIconCache.containsKey(packageNameWithUser)) { + return requireNotNull(packageIconCache[packageNameWithUser]) } - - if (currentDiscreteAccesses.isNotEmpty()) { - val opName = currentDiscreteAccesses.last().opName - clusters.add( - AppPermissionDiscreteAccessCluster( - appPermAccesses.appPermissionId, - appPermAccesses.attributionLabel, - appPermAccesses.attributionTags, - currentDiscreteAccesses.toMutableList(), - if (isOpClusteredByItself(opName)) opName else null - ) - ) + val packageIcon = packageRepository.getBadgedPackageIcon(packageName, userHandle) + if (packageIcon != null) { + packageIconCache[packageNameWithUser] = packageIcon } - return clusters - } - /** - * Returns whether the provided [DiscreteAccess] occurred close enough to those in the clustered - * list that it can be added to the cluster. - */ - private fun canAccessBeAddedToCluster( - currentAccess: DiscreteAccess, - clusteredAccesses: List<DiscreteAccess> - ): Boolean { - val clusterOp = clusteredAccesses.last().opName - if ( - (isOpClusteredByItself(currentAccess.opName) || isOpClusteredByItself(clusterOp)) && - currentAccess.opName != clusteredAccesses.last().opName - ) { - return false - } - val currentAccessMinute = currentAccess.accessTimeMs / ONE_MINUTE_MS - val prevMostRecentAccessMillis = - clusteredAccesses.maxOf { discreteAccess -> - if (discreteAccess.accessDurationMs > 0) - discreteAccess.accessTimeMs + discreteAccess.accessDurationMs - ONE_MINUTE_MS - else discreteAccess.accessTimeMs - } - val prevMostRecentAccessMinute = prevMostRecentAccessMillis / ONE_MINUTE_MS - return (currentAccessMinute - prevMostRecentAccessMinute) <= CLUSTER_SPACING_MINUTES + return packageIcon } - /** - * Determine if an op should be in its own cluster and hence display as an individual entry in - * the privacy timeline - */ - private fun isOpClusteredByItself(opName: String): Boolean { - if (isLocationByPassEnabled()) { - return opName == AppOpsManager.OPSTR_EMERGENCY_LOCATION + private fun getDurationSummary(durationMs: Long): String? { + // Only show the duration summary if it is at least (CLUSTER_SPACING_MINUTES + 1) minutes. + // Displaying a time that is shorter than the cluster granularity + // (CLUSTER_SPACING_MINUTES) will not convey useful information. + if (durationMs >= TimeUnit.MINUTES.toMillis(CLUSTER_SPACING_MINUTES + 1)) { + return getDurationUsedStr(application, durationMs) } - return false + return null } - /** - * Composes all UI information from a [AppPermissionDiscreteAccessCluster] into a - * [AppPermissionAccessUiInfo]. - */ - private fun AppPermissionDiscreteAccessCluster.buildAppPermissionAccessUiInfo(): - AppPermissionAccessUiInfo { - val context = application - // The end minute is exclusive here in terms of access, i.e. [1..5) as the private data - // was not accessed at minute 5, it helps calculate the duration correctly. - val accessEndTimeMillis = - discreteAccesses.maxOf { appOpEvent -> - if (appOpEvent.accessDurationMs > 0) - appOpEvent.accessTimeMs + appOpEvent.accessDurationMs - else appOpEvent.accessTimeMs + ONE_MINUTE_MS - } - val accessStartTimeMillis = discreteAccesses.minOf { it.accessTimeMs } - val durationMs = accessEndTimeMillis - accessStartTimeMillis - val durationSummaryLabel = - if (durationMs >= TimeUnit.MINUTES.toMillis(CLUSTER_SPACING_MINUTES + 1)) { - getDurationUsedStr(context, durationMs) - } else null - - val proxyLabel = getProxyPackageLabel(this) - val subAttributionLabel = getSubAttributionLabel(this) - val showingSubAttribution = !subAttributionLabel.isNullOrEmpty() - val summary = - buildUsageSummary(context, subAttributionLabel, proxyLabel, durationSummaryLabel) - val isEmergencyLocationAccess = - isLocationByPassEnabled() && clusteredOp == AppOpsManager.OPSTR_EMERGENCY_LOCATION - - return AppPermissionAccessUiInfo( - this.appPermissionId.userHandle, - this.appPermissionId.packageName, - getPackageLabel(this.appPermissionId.packageName, this.appPermissionId.userHandle), - permissionGroup, - accessStartTimeMillis, - // Make the end time inclusive i.e. [1..4] - accessEndTimeMillis - ONE_MINUTE_MS, - summary, - showingSubAttribution, - this.attributionTags.toSet(), - getBadgedPackageIcon(this.appPermissionId.packageName, this.appPermissionId.userHandle), - isEmergencyLocationAccess - ) - } - - /** Builds a summary of the permission access. */ private fun buildUsageSummary( - context: Context, subAttributionLabel: String?, proxyPackageLabel: String?, - durationSummary: String? + durationSummary: String?, ): String? { val subTextStrings: MutableList<String> = mutableListOf() - subAttributionLabel?.let { subTextStrings.add(subAttributionLabel) } proxyPackageLabel?.let { subTextStrings.add(it) } durationSummary?.let { subTextStrings.add(it) } return when (subTextStrings.size) { 3 -> - context.getString( + application.getString( R.string.history_preference_subtext_3, subTextStrings[0], subTextStrings[1], - subTextStrings[2] + subTextStrings[2], ) 2 -> - context.getString( + application.getString( R.string.history_preference_subtext_2, subTextStrings[0], - subTextStrings[1] + subTextStrings[1], ) 1 -> subTextStrings[0] else -> null } } - /** Returns whether app subattribution should be shown. */ - private fun shouldShowSubAttributionForApp(lightPackageInfo: LightPackageInfo?): Boolean { - return lightPackageInfo != null && - shouldShowSubattributionInPermissionsDashboard() && - SubattributionUtils.isSubattributionSupported(lightPackageInfo) - } - - /** Returns the proxied package label if the permission access was proxied. */ - private fun getProxyPackageLabel(accessCluster: AppPermissionDiscreteAccessCluster): String? = - accessCluster.discreteAccesses - .firstOrNull { it.proxy?.packageName != null } - ?.let { - getPackageLabel( - it.proxy!!.packageName!!, - UserHandle.getUserHandleForUid(it.proxy.uid) - ) - } - - /** Returns the attribution label for the permission access, if any. */ - private fun getSubAttributionLabel(accessCluster: AppPermissionDiscreteAccessCluster): String? { - // Special case for EMERGENCY_LOCATION app op. Show enforced attribution label in the - // Privacy Dashboard - if ( - isLocationByPassEnabled() && - accessCluster.clusteredOp == AppOpsManager.OPSTR_EMERGENCY_LOCATION - ) { - return application.getString( - R.string.privacy_dashboard_emergency_location_enforced_attribution_label - ) - } - - return if (accessCluster.attributionLabel == Resources.ID_NULL) null - else { - val lightPackageInfo = getLightPackageInfo(accessCluster.appPermissionId) - getSubAttributionLabels(lightPackageInfo)?.get(accessCluster.attributionLabel) - } - } - - private fun getSubAttributionLabels(lightPackageInfo: LightPackageInfo?): Map<Int, String>? = - if (lightPackageInfo == null) null - else SubattributionUtils.getAttributionLabels(application, lightPackageInfo) - - private fun getLightPackageInfo(appPermissionId: AppPermissionId) = - lightPackageInfoLiveDataMap[Pair(appPermissionId.packageName, appPermissionId.userHandle)] - ?.value - - private fun getLightPackageInfo(packageName: String, userHandle: UserHandle) = - lightPackageInfoLiveDataMap[Pair(packageName, userHandle)]?.value - - private fun AllLightHistoricalPackageOpsLiveData.getLightHistoricalPackageOps() = - this.value?.values - - /** Data used to create a preference for an app's permission usage. */ - data class AppPermissionAccessUiInfo( - val userHandle: UserHandle, - val packageName: String, - val packageLabel: String, - val permissionGroup: String, - val accessStartTime: Long, - val accessEndTime: Long, - val summaryText: CharSequence?, - val showingAttribution: Boolean, - val attributionTags: Set<String>, - val badgedPackageIcon: Drawable?, - val isEmergencyLocationAccess: Boolean - ) - - sealed class PermissionUsageDetailsUiState { - data object Loading : PermissionUsageDetailsUiState() - - data class Success( - val appPermissionAccessUiInfoList: List<AppPermissionAccessUiInfo>, - val containsSystemAppUsage: Boolean, - val showSystem: Boolean, - val show7Days: Boolean, - ) : PermissionUsageDetailsUiState() - } - - /** - * Data class representing a cluster of permission accesses close enough together to be - * displayed as a single access in the UI. - */ - private data class AppPermissionDiscreteAccessCluster( - val appPermissionId: AppPermissionId, - val attributionLabel: Int, - val attributionTags: List<String>, - val discreteAccesses: List<DiscreteAccess>, - val clusteredOp: String? - ) - - /** - * Data class representing all permission accesses for a particular package, user, permission - * and attribution label. - */ - private data class AppPermissionDiscreteAccessesWithLabel( - val appPermissionId: AppPermissionId, - val attributionLabel: Int, - val attributionTags: List<String>, - val discreteAccesses: List<DiscreteAccess> - ) - - /** [LiveData] object for [PermissionUsageDetailsUiState]. */ - private val _permissionUsagesDetailsInfoUiLiveData = - object : - SmartUpdateMediatorLiveData<@JvmSuppressWildcards PermissionUsageDetailsUiState>() { - private val getAppPermGroupUiInfoLiveData = { appPermissionId: AppPermissionId -> - AppPermGroupUiInfoLiveData[ - Triple( - appPermissionId.packageName, - appPermissionId.permissionGroup, - appPermissionId.userHandle, - )] - } - private val getLightPackageInfoLiveData = - { packageWithUserHandle: Pair<String, UserHandle> -> - LightPackageInfoLiveData[packageWithUserHandle] - } - - init { - addSource(allLightHistoricalPackageOpsLiveData) { update() } - addSource(showSystemLiveData) { update() } - addSource(show7DaysLiveData) { update() } - } - - override fun onUpdate() { - if (!allLightHistoricalPackageOpsLiveData.isInitialized) { - return - } - - val appPermissionIds = mutableSetOf<AppPermissionId>() - val allPackages: Set<Pair<String, UserHandle>> = - allLightHistoricalPackageOpsLiveData.value?.keys ?: setOf() - for (packageWithUserHandle: Pair<String, UserHandle> in allPackages) { - val appPermGroupIds = - allLightHistoricalPackageOpsLiveData.value - ?.get(packageWithUserHandle) - ?.appPermissionDiscreteAccesses - ?.map { it.appPermissionId } - ?.toSet() ?: setOf() - - appPermissionIds.addAll(appPermGroupIds) - } - - setSourcesToDifference( - appPermissionIds, - appPermGroupUiInfoLiveDataList, - getAppPermGroupUiInfoLiveData - ) { - update() - } - setSourcesToDifference( - allPackages, - lightPackageInfoLiveDataMap, - getLightPackageInfoLiveData - ) { - update() - } - - if (appPermGroupUiInfoLiveDataList.any { it.value.isStale }) { - return - } - - if (lightPackageInfoLiveDataMap.any { it.value.isStale }) { - return - } - - value = buildPermissionUsageDetailsUiInfo() - } - } - - override fun getPermissionUsagesDetailsInfoUiLiveData(): - LiveData<PermissionUsageDetailsUiState> = _permissionUsagesDetailsInfoUiLiveData - - override fun getShowSystem(): Boolean = showSystemLiveData.value ?: false - - override fun getShow7Days(): Boolean = show7DaysLiveData.value ?: false - /** Companion object for [PermissionUsageDetailsViewModel]. */ companion object { - const val ONE_HOUR_MS = 3_600_000 const val ONE_MINUTE_MS = 60_000 const val CLUSTER_SPACING_MINUTES: Long = 1L - private const val TELECOM_PACKAGE = "com.android.server.telecom" val TIME_7_DAYS_DURATION: Long = DAYS.toMillis(7) val TIME_24_HOURS_DURATION: Long = DAYS.toMillis(1) internal const val SHOULD_SHOW_SYSTEM_KEY = "showSystem" @@ -694,7 +305,7 @@ class PermissionUsageDetailsViewModel( listOf( Manifest.permission_group.CAMERA, Manifest.permission_group.LOCATION, - Manifest.permission_group.MICROPHONE + Manifest.permission_group.MICROPHONE, ) .flatMap { group -> PermissionMapping.getPlatformPermissionNamesOfGroup(group) } .mapNotNull { permName -> AppOpsManager.permissionToOp(permName) } @@ -719,7 +330,7 @@ class PermissionUsageDetailsViewModel( accessStartTime: Long, accessEndTime: Long, showingAttribution: Boolean, - attributionTags: Set<String> + attributionTags: Set<String>, ): Intent { return getManagePermissionUsageIntent( context, @@ -728,7 +339,7 @@ class PermissionUsageDetailsViewModel( accessStartTime, accessEndTime, showingAttribution, - attributionTags + attributionTags, ) ?: getDefaultManageAppPermissionsIntent(packageName, userHandle) } @@ -736,6 +347,7 @@ class PermissionUsageDetailsViewModel( * Gets an [Intent.ACTION_MANAGE_PERMISSION_USAGE] intent, or null if attribution shouldn't * be shown or the intent can't be handled. */ + @Suppress("DEPRECATION") private fun getManagePermissionUsageIntent( context: Context, packageName: String, @@ -743,7 +355,7 @@ class PermissionUsageDetailsViewModel( accessStartTime: Long, accessEndTime: Long, showingAttribution: Boolean, - attributionTags: Set<String> + attributionTags: Set<String>, ): Intent? { if ( !showingAttribution || @@ -768,13 +380,13 @@ class PermissionUsageDetailsViewModel( val resolveInfo = context.packageManager.resolveActivity( intent, - PackageManager.ResolveInfoFlags.of(0) + PackageManager.ResolveInfoFlags.of(0), ) if ( resolveInfo?.activityInfo == null || !Objects.equals( resolveInfo.activityInfo.permission, - Manifest.permission.START_VIEW_PERMISSION_USAGE + Manifest.permission.START_VIEW_PERMISSION_USAGE, ) ) { return null @@ -785,8 +397,9 @@ class PermissionUsageDetailsViewModel( private fun getDefaultManageAppPermissionsIntent( packageName: String, - userHandle: UserHandle + userHandle: UserHandle, ): Intent { + @Suppress("DEPRECATION") return Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS).apply { putExtra(Intent.EXTRA_USER, userHandle) putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) @@ -795,29 +408,65 @@ class PermissionUsageDetailsViewModel( private fun isLocationByPassEnabled(): Boolean = SdkLevel.isAtLeastV() && Flags.locationBypassPrivacyDashboardEnabled() + + fun create( + app: Application, + handle: SavedStateHandle, + permissionGroup: String, + ): PermissionUsageDetailsViewModel { + val permissionRepository = PermissionRepository.getInstance(app) + val packageRepository = PackageRepository.getInstance(app) + val appOpRepository = AppOpRepository.getInstance(app, permissionRepository) + val roleRepository = RoleRepository.getInstance(app) + val userRepository = UserRepository.getInstance(app) + val useCase = + GetPermissionGroupUsageDetailsUseCase( + permissionGroup, + packageRepository, + permissionRepository, + appOpRepository, + roleRepository, + userRepository, + ) + return PermissionUsageDetailsViewModel(app, useCase, handle, permissionGroup) + } + } + + /** Data used to create a preference for an app's permission usage. */ + data class AppPermissionAccessUiInfo( + val userHandle: UserHandle, + val packageName: String, + val packageLabel: String, + val permissionGroup: String, + val accessStartTime: Long, + val accessEndTime: Long, + val summaryText: CharSequence?, + val showingAttribution: Boolean, + val attributionTags: Set<String>, + val badgedPackageIcon: Drawable?, + val isEmergencyLocationAccess: Boolean, + ) + + sealed class PermissionUsageDetailsUiState { + data object Loading : PermissionUsageDetailsUiState() + + data class Success( + val appPermissionAccessUiInfoList: List<AppPermissionAccessUiInfo>, + val containsSystemAppUsage: Boolean, + val showSystem: Boolean, + val show7Days: Boolean, + ) : PermissionUsageDetailsUiState() } /** Factory for [PermissionUsageDetailsViewModel]. */ @RequiresApi(Build.VERSION_CODES.S) class PermissionUsageDetailsViewModelFactory( val app: Application, - owner: SavedStateRegistryOwner, private val permissionGroup: String, - ) : AbstractSavedStateViewModelFactory(owner, Bundle()) { - override fun <T : ViewModel> create( - key: String, - modelClass: Class<T>, - handle: SavedStateHandle, - ): T { + ) : ViewModelProvider.Factory { + override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T { @Suppress("UNCHECKED_CAST") - return if ( - com.android.permission.flags.Flags.livedataRefactorPermissionTimelineEnabled() - ) { - Log.d("PermissionTimeline", "timeline refactor flag enabled..") - PermissionUsageDetailsViewModelV2.create(app, handle, permissionGroup) as T - } else { - PermissionUsageDetailsViewModel(app, handle, permissionGroup) as T - } + return create(app, extras.createSavedStateHandle(), permissionGroup) as T } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModelV2.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModelV2.kt deleted file mode 100644 index 312a7ee20..000000000 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModelV2.kt +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.permissioncontroller.permission.ui.model.v31 - -import android.app.AppOpsManager.OPSTR_EMERGENCY_LOCATION -import android.app.Application -import android.graphics.drawable.Drawable -import android.os.UserHandle -import androidx.annotation.VisibleForTesting -import androidx.lifecycle.LiveData -import androidx.lifecycle.SavedStateHandle -import androidx.lifecycle.asLiveData -import androidx.lifecycle.viewModelScope -import com.android.permissioncontroller.DeviceUtils -import com.android.permissioncontroller.R -import com.android.permissioncontroller.appops.data.repository.v31.AppOpRepository -import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository -import com.android.permissioncontroller.permission.domain.model.v31.PermissionTimelineUsageModel -import com.android.permissioncontroller.permission.domain.model.v31.PermissionTimelineUsageModelWrapper -import com.android.permissioncontroller.permission.domain.usecase.v31.GetPermissionGroupUsageDetailsUseCase -import com.android.permissioncontroller.permission.domain.usecase.v31.isLocationByPassEnabled -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.SHOULD_SHOW_7_DAYS_KEY -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.SHOULD_SHOW_SYSTEM_KEY -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.TIME_24_HOURS_DURATION -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.TIME_7_DAYS_DURATION -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState -import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository -import com.android.permissioncontroller.role.data.repository.v31.RoleRepository -import com.android.permissioncontroller.user.data.repository.v31.UserRepository -import java.time.Instant -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.stateIn - -class PermissionUsageDetailsViewModelV2( - app: Application, - private val getPermissionUsageDetailsUseCase: GetPermissionGroupUsageDetailsUseCase, - private val state: SavedStateHandle, - private val permissionGroup: String, - scope: CoroutineScope? = null, - private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default, - private val packageRepository: PackageRepository = PackageRepository.getInstance(app) -) : BasePermissionUsageDetailsViewModel(app) { - private val coroutineScope = scope ?: viewModelScope - private val context = app - - private val showSystemFlow = MutableStateFlow(state[SHOULD_SHOW_SYSTEM_KEY] ?: false) - private val show7DaysFlow = MutableStateFlow(state[SHOULD_SHOW_7_DAYS_KEY] ?: false) - - private val permissionTimelineUsagesFlow: - StateFlow<PermissionTimelineUsageModelWrapper> by lazy { - getPermissionUsageDetailsUseCase(coroutineScope) - .flowOn(defaultDispatcher) - .stateIn( - coroutineScope, - SharingStarted.WhileSubscribed(5000), - PermissionTimelineUsageModelWrapper.Loading - ) - } - - @VisibleForTesting - val permissionUsageDetailsUiStateFlow: Flow<PermissionUsageDetailsUiState> by lazy { - combine(permissionTimelineUsagesFlow, showSystemFlow, show7DaysFlow) { - permissionTimelineUsages, - showSystem, - show7Days -> - permissionTimelineUsages.buildPermissionUsageDetailsUiInfo(showSystem, show7Days) - } - .flowOn(defaultDispatcher) - } - - override fun getPermissionUsagesDetailsInfoUiLiveData(): - LiveData<PermissionUsageDetailsUiState> { - return permissionUsageDetailsUiStateFlow.asLiveData( - context = coroutineScope.coroutineContext - ) - } - - private fun PermissionTimelineUsageModelWrapper.buildPermissionUsageDetailsUiInfo( - showSystem: Boolean, - show7Days: Boolean - ): PermissionUsageDetailsUiState { - if (this is PermissionTimelineUsageModelWrapper.Loading) { - return PermissionUsageDetailsUiState.Loading - } - val timelineUsageModels = - (this as PermissionTimelineUsageModelWrapper.Success).timelineUsageModels - val startTime = - (System.currentTimeMillis() - getUsageDuration(show7Days)).coerceAtLeast( - Instant.EPOCH.toEpochMilli() - ) - - val permissionTimelineUsageModels = - timelineUsageModels.filter { it.accessEndMillis > startTime } - val containsSystemUsages = permissionTimelineUsageModels.any { !it.isUserSensitive } - val result = - permissionTimelineUsageModels - .filter { showSystem || it.isUserSensitive } - .map { clusterOps -> - val durationSummaryLabel = - if (clusterOps.durationMillis > 0) { - getDurationSummary(clusterOps.durationMillis) - } else { - null - } - val proxyLabel = getProxyPackageLabel(clusterOps) - val isEmergencyLocationAccess = - isLocationByPassEnabled() && - clusterOps.opNames.any { it == OPSTR_EMERGENCY_LOCATION } - val subAttributionLabel = - if (isEmergencyLocationAccess) { - emergencyLocationAttributionLabel - } else { - clusterOps.attributionLabel - } - val showingSubAttribution = !subAttributionLabel.isNullOrEmpty() - val summary = - buildUsageSummary(subAttributionLabel, proxyLabel, durationSummaryLabel) - PermissionUsageDetailsViewModel.AppPermissionAccessUiInfo( - UserHandle.of(clusterOps.userId), - clusterOps.packageName, - getPackageLabel(clusterOps.packageName, UserHandle.of(clusterOps.userId)), - permissionGroup, - clusterOps.accessStartMillis, - clusterOps.accessEndMillis, - summary, - showingSubAttribution, - clusterOps.attributionTags ?: emptySet(), - getBadgedPackageIcon( - clusterOps.packageName, - UserHandle.of(clusterOps.userId) - ), - isEmergencyLocationAccess - ) - } - .sortedBy { -1 * it.accessStartTime } - return PermissionUsageDetailsUiState.Success( - result, - containsSystemUsages, - showSystem, - show7Days - ) - } - - private val emergencyLocationAttributionLabel: String by lazy { - context.getString(R.string.privacy_dashboard_emergency_location_enforced_attribution_label) - } - - override fun getShowSystem(): Boolean = showSystemFlow.value - - override val showSystemLiveData = - showSystemFlow.asLiveData(context = coroutineScope.coroutineContext) - - override fun getShow7Days(): Boolean = show7DaysFlow.value - - private fun getUsageDuration(show7Days: Boolean): Long { - return if (show7Days && DeviceUtils.isHandheld()) { - TIME_7_DAYS_DURATION - } else { - TIME_24_HOURS_DURATION - } - } - - private fun getProxyPackageLabel(accessCluster: PermissionTimelineUsageModel): String? = - accessCluster.proxyPackageName?.let { proxyPackageName -> - if (accessCluster.proxyUserId != null) { - getPackageLabel(proxyPackageName, UserHandle.of(accessCluster.proxyUserId)) - } else null - } - - override fun updateShowSystemAppsToggle(showSystem: Boolean) { - if (showSystem != state[SHOULD_SHOW_SYSTEM_KEY]) { - state[SHOULD_SHOW_SYSTEM_KEY] = showSystem - } - showSystemFlow.compareAndSet(!showSystem, showSystem) - } - - override fun updateShow7DaysToggle(show7Days: Boolean) { - if (show7Days != state[SHOULD_SHOW_7_DAYS_KEY]) { - state[SHOULD_SHOW_7_DAYS_KEY] = show7Days - } - show7DaysFlow.compareAndSet(!show7Days, show7Days) - } - - // TODO review these methods when old impl is removed, suspend function?? - override fun getPackageLabel(app: Application, packageName: String, user: UserHandle): String { - return packageRepository.getPackageLabel(packageName, user) - } - - override fun getBadgedPackageIcon( - app: Application, - packageName: String, - user: UserHandle - ): Drawable? { - return packageRepository.getBadgedPackageIcon(packageName, user) - } - - companion object { - fun create( - app: Application, - handle: SavedStateHandle, - permissionGroup: String - ): PermissionUsageDetailsViewModelV2 { - val permissionRepository = PermissionRepository.getInstance(app) - val packageRepository = PackageRepository.getInstance(app) - val appOpRepository = AppOpRepository.getInstance(app, permissionRepository) - val roleRepository = RoleRepository.getInstance(app) - val userRepository = UserRepository.getInstance(app) - val useCase = - GetPermissionGroupUsageDetailsUseCase( - permissionGroup, - packageRepository, - permissionRepository, - appOpRepository, - roleRepository, - userRepository - ) - return PermissionUsageDetailsViewModelV2(app, useCase, handle, permissionGroup) - } - } -} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsFragment.kt index 2ade4863a..2d302c2b4 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsFragment.kt @@ -29,7 +29,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity -import com.android.permissioncontroller.permission.ui.model.v31.BasePermissionUsageDetailsViewModel +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsViewModelFactory /** @@ -42,7 +42,7 @@ class WearPermissionUsageDetailsFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View? { val permissionGroup = arguments?.getString(Intent.EXTRA_PERMISSION_GROUP_NAME) @@ -56,11 +56,10 @@ class WearPermissionUsageDetailsFragment : Fragment() { val factory = PermissionUsageDetailsViewModelFactory( PermissionControllerApplication.get(), - this, - permissionGroup + permissionGroup, ) val viewModel = - ViewModelProvider(this, factory).get(BasePermissionUsageDetailsViewModel::class.java) + ViewModelProvider(this, factory).get(PermissionUsageDetailsViewModel::class.java) viewModel.updateShowSystemAppsToggle(showSystem) return ComposeView(requireContext()).apply { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt index 279eaa8cc..adcf9323f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt @@ -31,7 +31,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.ui.model.v31.BasePermissionUsageDetailsViewModel import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.AppPermissionAccessUiInfo import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState @@ -45,7 +44,7 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils @Composable fun WearPermissionUsageDetailsScreen( permissionGroup: String, - viewModel: BasePermissionUsageDetailsViewModel, + viewModel: PermissionUsageDetailsViewModel, ) { val context = LocalContext.current val uiData = viewModel.getPermissionUsagesDetailsInfoUiLiveData().observeAsState(null) diff --git a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/ui/model/PermissionUsageDetailsViewModelTest.kt b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/ui/model/PermissionUsageDetailsViewModelTest.kt index eb5fdefdb..644bdfd15 100644 --- a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/ui/model/PermissionUsageDetailsViewModelTest.kt +++ b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/ui/model/PermissionUsageDetailsViewModelTest.kt @@ -16,16 +16,14 @@ package com.android.permissioncontroller.permission.ui.model -import android.platform.test.annotations.RequiresFlagsEnabled import android.platform.test.flag.junit.DeviceFlagsValueProvider import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.SavedStateHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.modules.utils.build.SdkLevel -import com.android.permission.flags.Flags import com.android.permissioncontroller.PermissionControllerApplication +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModelV2 import com.google.common.truth.Truth import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -42,15 +40,14 @@ class PermissionUsageDetailsViewModelTest { @JvmField @Rule val instantTaskExecutorRule = InstantTaskExecutorRule() @Test - @RequiresFlagsEnabled(Flags.FLAG_LIVEDATA_REFACTOR_PERMISSION_TIMELINE_ENABLED) fun verifyUiStateIsGeneratedSuccessfully() { Assume.assumeTrue(SdkLevel.isAtLeastS()) lateinit var uiState: PermissionUsageDetailsUiState.Success val viewModel = - PermissionUsageDetailsViewModelV2.create( + PermissionUsageDetailsViewModel.create( PermissionControllerApplication.get(), SavedStateHandle(mapOf("show7Days" to true, "showSystem" to true)), - LOCATION_PERMISSION_GROUP + LOCATION_PERMISSION_GROUP, ) val countDownLatch = CountDownLatch(1) diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageDetailsViewModelTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageDetailsViewModelTest.kt index edaea9aba..4334026c3 100644 --- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageDetailsViewModelTest.kt +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageDetailsViewModelTest.kt @@ -30,8 +30,8 @@ import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.appops.data.model.v31.DiscretePackageOpsModel import com.android.permissioncontroller.appops.data.model.v31.DiscretePackageOpsModel.DiscreteOpModel import com.android.permissioncontroller.permission.domain.usecase.v31.GetPermissionGroupUsageDetailsUseCase +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModelV2 import com.android.permissioncontroller.permission.utils.LocationUtils import com.android.permissioncontroller.permission.utils.StringUtils import com.android.permissioncontroller.permission.utils.Utils @@ -67,7 +67,7 @@ import org.mockito.quality.Strictness /** * These unit tests are for new permission timeline implementation, the new view model class is - * [PermissionUsageDetailsViewModelV2] + * [PermissionUsageDetailsViewModel] */ @RunWith(AndroidJUnit4::class) class PermissionUsageDetailsViewModelTest { @@ -107,7 +107,7 @@ class PermissionUsageDetailsViewModelTest { any(), anyInt(), anyInt(), - any(Array<String>::class.java) + any(Array<String>::class.java), ) ) .thenReturn("Duration Summary") @@ -119,7 +119,7 @@ class PermissionUsageDetailsViewModelTest { systemPackageName to getPackageInfoModel( systemPackageName, - applicationFlags = ApplicationInfo.FLAG_SYSTEM + applicationFlags = ApplicationInfo.FLAG_SYSTEM, ), ) .toMutableMap() @@ -127,7 +127,7 @@ class PermissionUsageDetailsViewModelTest { packageRepository = FakePackageRepository( packageInfos, - packagesAndLabels = mapOf(testPackageName to testPackageLabel) + packagesAndLabels = mapOf(testPackageName to testPackageLabel), ) } @@ -140,9 +140,7 @@ class PermissionUsageDetailsViewModelTest { fun verifyOnlyNonSystemAppsAreShown() = runTest { val accessTimeMillis = (getCurrentTime() - TimeUnit.HOURS.toMillis(5)) val appOpEvents = - listOf( - DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, accessTimeMillis, -1), - ) + listOf(DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, accessTimeMillis, -1)) val discretePackageOps = flow { emit( listOf( @@ -176,8 +174,8 @@ class PermissionUsageDetailsViewModelTest { DiscreteOpModel( AppOpsManager.OPSTR_COARSE_LOCATION, accessTimeMillis, - TimeUnit.MINUTES.toMillis(1) - ), + TimeUnit.MINUTES.toMillis(1), + ) ) val discretePackageOps = flow { emit( @@ -192,7 +190,7 @@ class PermissionUsageDetailsViewModelTest { getViewModel( LOCATION_PERMISSION_GROUP, discretePackageOps, - savedStateMap = mapOf("show7Days" to false, "showSystem" to true) + savedStateMap = mapOf("show7Days" to false, "showSystem" to true), ) val uiState = getPermissionUsageDetailsUiState(underTest) @@ -210,13 +208,13 @@ class PermissionUsageDetailsViewModelTest { DiscreteOpModel( AppOpsManager.OPSTR_COARSE_LOCATION, accessTimeMillis, - TimeUnit.MINUTES.toMillis(1) - ), + TimeUnit.MINUTES.toMillis(1), + ) ) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -225,7 +223,7 @@ class PermissionUsageDetailsViewModelTest { getViewModel( LOCATION_PERMISSION_GROUP, discretePackageOps, - savedStateMap = mapOf("show7Days" to false, "showSystem" to false) + savedStateMap = mapOf("show7Days" to false, "showSystem" to false), ) val uiState = getPermissionUsageDetailsUiState(underTest) @@ -240,13 +238,13 @@ class PermissionUsageDetailsViewModelTest { DiscreteOpModel( AppOpsManager.OPSTR_COARSE_LOCATION, accessTimeMillis, - TimeUnit.MINUTES.toMillis(1) - ), + TimeUnit.MINUTES.toMillis(1), + ) ) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(systemPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(systemPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -255,7 +253,7 @@ class PermissionUsageDetailsViewModelTest { getViewModel( LOCATION_PERMISSION_GROUP, discretePackageOps, - savedStateMap = mapOf("show7Days" to false, "showSystem" to false) + savedStateMap = mapOf("show7Days" to false, "showSystem" to false), ) val uiState = getPermissionUsageDetailsUiState(underTest) @@ -271,18 +269,18 @@ class PermissionUsageDetailsViewModelTest { DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, accessStartWithIn24Hours, - TimeUnit.MINUTES.toMillis(5) + TimeUnit.MINUTES.toMillis(5), ), DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, accessStartBefore24Hours, - TimeUnit.MINUTES.toMillis(7) + TimeUnit.MINUTES.toMillis(7), ), ) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -304,18 +302,18 @@ class PermissionUsageDetailsViewModelTest { DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, accessTimeWithIn24Hours, - TimeUnit.MINUTES.toMillis(5) + TimeUnit.MINUTES.toMillis(5), ), DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, accessTimeBefore24Hours, - TimeUnit.MINUTES.toMillis(7) + TimeUnit.MINUTES.toMillis(7), ), ) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -344,13 +342,13 @@ class PermissionUsageDetailsViewModelTest { DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, accessTimeMillis, - TimeUnit.MINUTES.toMillis(5) - ), + TimeUnit.MINUTES.toMillis(5), + ) ) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -371,13 +369,13 @@ class PermissionUsageDetailsViewModelTest { DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, accessTimeMillis, - TimeUnit.MINUTES.toMillis(1) - ), + TimeUnit.MINUTES.toMillis(1), + ) ) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -397,22 +395,16 @@ class PermissionUsageDetailsViewModelTest { whenever(application.getString(anyInt())).thenReturn("emergency attr label") val accessTimeMillis = (getCurrentTime() - TimeUnit.HOURS.toMillis(5)) val appOpEvents = - listOf( - DiscreteOpModel(AppOpsManager.OPSTR_EMERGENCY_LOCATION, accessTimeMillis, -1), - ) + listOf(DiscreteOpModel(AppOpsManager.OPSTR_EMERGENCY_LOCATION, accessTimeMillis, -1)) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } - val underTest = - getViewModel( - LOCATION_PERMISSION_GROUP, - discretePackageOps, - ) + val underTest = getViewModel(LOCATION_PERMISSION_GROUP, discretePackageOps) val uiState = getPermissionUsageDetailsUiState(underTest) assertThat(uiState.appPermissionAccessUiInfoList.size).isEqualTo(1) val timelineRow = uiState.appPermissionAccessUiInfoList.first() @@ -429,21 +421,15 @@ class PermissionUsageDetailsViewModelTest { DiscreteOpModel( AppOpsManager.OPSTR_COARSE_LOCATION, accessTimeMillis, - TimeUnit.MINUTES.toMillis(1) - ), + TimeUnit.MINUTES.toMillis(1), + ) ) val actualData = - listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), - ) + listOf(DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents)) val discretePackageOps = MutableStateFlow(emptyList<DiscretePackageOpsModel>()) discretePackageOps.emit(emptyList()) - val underTest = - getViewModel( - LOCATION_PERMISSION_GROUP, - discretePackageOps, - ) + val underTest = getViewModel(LOCATION_PERMISSION_GROUP, discretePackageOps) val result by collectLastValue(underTest.permissionUsageDetailsUiStateFlow) var uiState = result as PermissionUsageDetailsUiState.Success assertThat(uiState.appPermissionAccessUiInfoList).isEmpty() @@ -463,22 +449,18 @@ class PermissionUsageDetailsViewModelTest { DiscreteOpModel( AppOpsManager.OPSTR_COARSE_LOCATION, accessTimeMillis, - TimeUnit.MINUTES.toMillis(1) - ), + TimeUnit.MINUTES.toMillis(1), + ) ) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } - val underTest = - getViewModel( - LOCATION_PERMISSION_GROUP, - discretePackageOps, - ) + val underTest = getViewModel(LOCATION_PERMISSION_GROUP, discretePackageOps) val uiState = getPermissionUsageDetailsUiState(underTest) assertThat(uiState.show7Days).isFalse() @@ -517,20 +499,20 @@ class PermissionUsageDetailsViewModelTest { permissionGroup: String, discretePackageOps: Flow<List<DiscretePackageOpsModel>>, savedStateMap: Map<String, Boolean> = mapOf("show7Days" to false, "showSystem" to false), - pkgRepository: PackageRepository = packageRepository + pkgRepository: PackageRepository = packageRepository, ) = - PermissionUsageDetailsViewModelV2( + PermissionUsageDetailsViewModel( application, getPermissionGroupUsageDetailsUseCase(permissionGroup, discretePackageOps), SavedStateHandle(savedStateMap), permissionGroup, scope = backgroundScope, StandardTestDispatcher(testScheduler), - packageRepository = pkgRepository + packageRepository = pkgRepository, ) private fun TestScope.getPermissionUsageDetailsUiState( - viewModel: PermissionUsageDetailsViewModelV2 + viewModel: PermissionUsageDetailsViewModel ): PermissionUsageDetailsUiState.Success { val result by collectLastValue(viewModel.permissionUsageDetailsUiStateFlow) return result as PermissionUsageDetailsUiState.Success @@ -542,7 +524,7 @@ class PermissionUsageDetailsViewModelTest { permissionsFlags: List<Int> = listOf( PackageInfo.REQUESTED_PERMISSION_GRANTED, - PackageInfo.REQUESTED_PERMISSION_GRANTED + PackageInfo.REQUESTED_PERMISSION_GRANTED, ), applicationFlags: Int = 0, ) = PackageInfoModel(packageName, requestedPermissions, permissionsFlags, applicationFlags) |