summaryrefslogtreecommitdiff
path: root/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt
blob: 4a2d3b68a34b1278be618166ff3589b3f6190cc3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
 * Copyright (C) 2019 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

import android.Manifest.permission.MANAGE_EXTERNAL_STORAGE
import android.Manifest.permission_group.STORAGE
import android.app.AppOpsManager
import android.app.AppOpsManager.MODE_ALLOWED
import android.app.AppOpsManager.MODE_DEFAULT
import android.app.AppOpsManager.MODE_FOREGROUND
import android.app.AppOpsManager.OPSTR_LEGACY_STORAGE
import android.app.AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE
import android.app.Application
import android.os.Build
import android.os.UserHandle
import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
import kotlinx.coroutines.Job

/**
 * A liveData which tracks all packages in the system which have full file permissions, as
 * represented by the OPSTR_LEGACY_STORAGE app op, not just media-only storage permissions.
 */
object FullStoragePermissionAppsLiveData :
    SmartAsyncMediatorLiveData<List<FullStoragePermissionAppsLiveData.FullStoragePackageState>>() {

    private val app: Application = PermissionControllerApplication.get()
    private val standardPermGroupsPackagesLiveData =
        PermGroupsPackagesLiveData.get(customGroups = false)

    data class FullStoragePackageState(
        val packageName: String,
        val user: UserHandle,
        val isLegacy: Boolean,
        val isGranted: Boolean
    )

    init {
        addSource(standardPermGroupsPackagesLiveData) { updateAsync() }
        addSource(AllPackageInfosLiveData) { updateAsync() }
    }

    override suspend fun loadDataAndPostValue(job: Job) {
        val storagePackages = standardPermGroupsPackagesLiveData.value?.get(STORAGE) ?: return
        val appOpsManager = app.getSystemService(AppOpsManager::class.java) ?: return

        val fullStoragePackages = mutableListOf<FullStoragePackageState>()
        for ((user, packageInfoList) in AllPackageInfosLiveData.value ?: emptyMap()) {
            val userPackages =
                packageInfoList.filter {
                    storagePackages.contains(it.packageName to user) ||
                        it.requestedPermissions.contains(MANAGE_EXTERNAL_STORAGE)
                }

            for (packageInfo in userPackages) {
                fullStoragePackages.add(
                    getFullStorageStateForPackage(appOpsManager, packageInfo, user) ?: continue
                )
            }
        }

        postValue(fullStoragePackages)
    }

    /**
     * Gets the full storage package information for a given package
     *
     * @param appOpsManager The App Ops manager to use, if applicable
     * @param packageInfo The package whose state is to be determined
     * @param userHandle A preexisting UserHandle object to use. Otherwise, one will be created
     * @return the FullStoragePackageState for the package, or null if the package does not request
     *   full storage permissions
     */
    fun getFullStorageStateForPackage(
        appOpsManager: AppOpsManager,
        packageInfo: LightPackageInfo,
        userHandle: UserHandle? = null
    ): FullStoragePackageState? {
        val sdk = packageInfo.targetSdkVersion
        val user = userHandle ?: UserHandle.getUserHandleForUid(packageInfo.uid)
        if (sdk < Build.VERSION_CODES.P) {
            return FullStoragePackageState(
                packageInfo.packageName,
                user,
                isLegacy = true,
                isGranted = true
            )
        } else if (
            sdk <= Build.VERSION_CODES.Q &&
                appOpsManager.unsafeCheckOpNoThrow(
                    OPSTR_LEGACY_STORAGE,
                    packageInfo.uid,
                    packageInfo.packageName
                ) == MODE_ALLOWED
        ) {
            return FullStoragePackageState(
                packageInfo.packageName,
                user,
                isLegacy = true,
                isGranted = true
            )
        }
        if (MANAGE_EXTERNAL_STORAGE in packageInfo.requestedPermissions) {
            val mode =
                appOpsManager.unsafeCheckOpNoThrow(
                    OPSTR_MANAGE_EXTERNAL_STORAGE,
                    packageInfo.uid,
                    packageInfo.packageName
                )
            val granted =
                mode == MODE_ALLOWED ||
                    mode == MODE_FOREGROUND ||
                    (mode == MODE_DEFAULT &&
                        MANAGE_EXTERNAL_STORAGE in packageInfo.grantedPermissions)
            return FullStoragePackageState(
                packageInfo.packageName,
                user,
                isLegacy = false,
                isGranted = granted
            )
        }
        return null
    }

    /** Recalculate the LiveData TODO ntmyren: Make livedata properly observe app ops */
    fun recalculate() {
        updateAsync()
    }
}