Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3220af70 authored by Eugene Susla's avatar Eugene Susla Committed by Android (Google) Code Review
Browse files

Merge "Dump auto revoke state" into rvc-dev

parents dfcf2e00 fd48c0be
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -13,3 +13,10 @@
}

-keep class com.android.car.ui.** {*;}

# for proto names for Proto.toString
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite {
  *** get*();
  *** set*(***);
  *** has*();
}
 No newline at end of file
+3 −6
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.permissioncontroller
import android.util.Log
import com.android.permissioncontroller.Constants.LOGS_TO_DUMP_FILE
import java.io.File
import java.io.PrintWriter

/**
 * Like {@link Log} but stores the logs in a file which can later be dumped via {@link #dump}
@@ -90,13 +89,11 @@ object DumpableLog {
    }

    /**
     * Write the previously logged entries to the print writer.
     *
     * @param pw the writer to dump to
     * @return the previously logged entries
     */
    fun dump(pw: PrintWriter) {
    suspend fun get(): List<String> {
        synchronized(lock) {
            file.forEachLine { pw.println(it) }
            return file.readLines()
        }
    }
}
 No newline at end of file
+26 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.
 */
syntax = "proto2";
package com.android.permissioncontroller;
option java_outer_classname = "PermissionControllerProto";

import "packages/apps/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.proto";

message PermissionControllerDumpProto {
  optional permission.service.AutoRevokePermissionsDumpProto autoRevoke = 1;

  repeated string logs = 3;
}
+257 −0
Original line number Diff line number Diff line
@@ -86,11 +86,17 @@ import com.android.permissioncontroller.permission.data.AllPackageInfosLiveData
import com.android.permissioncontroller.permission.data.AppOpLiveData
import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData
import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
import com.android.permissioncontroller.permission.data.UnusedAutoRevokedPackagesLiveData
import com.android.permissioncontroller.permission.data.UsageStatsLiveData
import com.android.permissioncontroller.permission.data.get
import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
import com.android.permissioncontroller.permission.service.AutoRevokePermissionsProto.AutoRevokePermissionsDumpProto
import com.android.permissioncontroller.permission.service.AutoRevokePermissionsProto.PackageProto
import com.android.permissioncontroller.permission.service.AutoRevokePermissionsProto.PerUserProto
import com.android.permissioncontroller.permission.service.AutoRevokePermissionsProto.PermissionGroupProto
import com.android.permissioncontroller.permission.service.AutoRevokePermissionsProto.TeamFoodSettingsProto
import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity
import com.android.permissioncontroller.permission.utils.IPC
import com.android.permissioncontroller.permission.utils.KotlinUtils
@@ -103,6 +109,7 @@ import com.android.permissioncontroller.permission.utils.updatePermissionFlags
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.concurrent.TimeUnit.DAYS
@@ -152,6 +159,25 @@ fun isAutoRevokeEnabled(context: Context): Boolean {
            getUnusedThresholdMs(context) != Long.MAX_VALUE
}

/**
 * @return dump of auto revoke service as a proto
 */
suspend fun dumpAutoRevokePermissions(context: Context): AutoRevokePermissionsDumpProto {
    val teamFoodSettings = GlobalScope.async(IPC) {
        TeamfoodSettings.get(context)?.dump()
                ?: TeamFoodSettingsProto.newBuilder().build()
    }

    val dumpData = GlobalScope.async(IPC) {
        AutoRevokeDumpLiveData(context).getInitializedValue(staleOk = true)
    }

    return AutoRevokePermissionsDumpProto.newBuilder()
            .setTeamfoodSettings(teamFoodSettings.await())
            .addAllUsers(dumpData.await().dumpUsers())
            .build()
}

/**
 * Receiver of the onBoot event.
 */
@@ -652,4 +678,235 @@ private data class TeamfoodSettings(
            }
        }
    }

    /**
     * @return team food settings for dumping as as a proto
     */
    suspend fun dump(): TeamFoodSettingsProto {
        return TeamFoodSettingsProto.newBuilder()
                .setEnabledForPreRApps(enabledForPreRApps)
                .setUnusedThresholdMillis(unusedThresholdMs)
                .setCheckFrequencyMillis(checkFrequencyMs)
                .build()
    }
}

/** Data interesting to auto-revoke */
private class AutoRevokeDumpLiveData(context: Context) :
        SmartUpdateMediatorLiveData<AutoRevokeDumpLiveData.AutoRevokeDumpData>() {
    /** All data */
    data class AutoRevokeDumpData(
        val users: List<AutoRevokeDumpUserData>
    ) {
        fun dumpUsers(): List<PerUserProto> {
            return users.map { it.dump() }
        }
    }

    /** Per user data */
    data class AutoRevokeDumpUserData(
        val user: UserHandle,
        val pkgs: List<AutoRevokeDumpPackageData>
    ) {
        fun dump(): PerUserProto {
            val dump = PerUserProto.newBuilder()
                    .setUserId(user.identifier)

            pkgs.forEach { dump.addPackages(it.dump()) }

            return dump.build()
        }
    }

    /** Per package data */
    data class AutoRevokeDumpPackageData(
        val uid: Int,
        val packageName: String,
        val firstInstallTime: Long,
        val lastTimeVisible: Long?,
        val groups: List<AutoRevokeDumpGroupData>
    ) {
        fun dump(): PackageProto {
            val dump = PackageProto.newBuilder()
                    .setUid(uid)
                    .setPackageName(packageName)
                    .setFirstInstallTime(firstInstallTime)

            lastTimeVisible?.let { dump.lastTimeVisible = lastTimeVisible }

            groups.forEach { dump.addGroups(it.dump()) }

            return dump.build()
        }
    }

    /** Per permission group data */
    data class AutoRevokeDumpGroupData(
        val groupName: String,
        val isFixed: Boolean,
        val isAnyGrantedIncludingAppOp: Boolean,
        val isGrantedByDefault: Boolean,
        val isGrantedByRole: Boolean,
        val isUserSensitive: Boolean,
        val isAutoRevoked: Boolean
    ) {
        fun dump(): PermissionGroupProto {
            return PermissionGroupProto.newBuilder()
                    .setGroupName(groupName)
                    .setIsFixed(isFixed)
                    .setIsAnyGrantedIncludingAppop(isAnyGrantedIncludingAppOp)
                    .setIsGrantedByDefault(isGrantedByDefault)
                    .setIsGrantedByRole(isGrantedByRole)
                    .setIsUserSensitive(isUserSensitive)
                    .setIsAutoRevoked(isAutoRevoked)
                    .build()
        }
    }

    /** Usage stats: user -> list<usages> */
    private val usages = UsageStatsLiveData[
        getUnusedThresholdMs(context),
        if (DEBUG_OVERRIDE_THRESHOLDS) INTERVAL_DAILY else INTERVAL_MONTHLY
    ]

    /** All package infos: user -> pkg **/
    private val packages = AllPackageInfosLiveData

    /** Group names of revoked permission groups: (user, pkg-name) -> set<group-name> **/
    private val revokedPermGroupNames = UnusedAutoRevokedPackagesLiveData

    /**
     * Group names for packages
     * map<user, pkg-name> -> list<perm-group-name>. {@code null} before step 1
     */
    private var pkgPermGroupNames:
            MutableMap<Pair<UserHandle, String>, PackagePermissionsLiveData>? = null

    /**
     * Group state for packages
     * map<(user, pkg-name) -> map<perm-group-name -> group>>, value {@code null} before step 2
     */
    private val pkgPermGroups =
            mutableMapOf<Pair<UserHandle, String>,
                    MutableMap<String, LightAppPermGroupLiveData>?>()

    /** If this live-data currently inside onUpdate */
    private var isUpdating = false

    init {
        addSource(revokedPermGroupNames) {
            updateIfActive()
        }

        addSource(usages) {
            updateIfActive()
        }

        addSource(packages) {
            pkgPermGroupNames?.values?.forEach { removeSource(it) }
            pkgPermGroupNames = null
            pkgPermGroups.values.forEach { it?.values?.forEach { removeSource(it) } }

            updateIfActive()
        }
    }

    override fun onUpdate() {
        // If a source is already ready, the call onUpdate when added. Suppress this
        if (isUpdating) {
            return
        }
        isUpdating = true

        // Step 1, packages is loaded, nothing else
        if (packages.isInitialized && pkgPermGroupNames == null) {
            pkgPermGroupNames = mutableMapOf()

            for ((user, userPkgs) in packages.value!!) {
                for (pkg in userPkgs) {
                    val newPermGroupNames = PackagePermissionsLiveData[pkg.packageName, user]
                    pkgPermGroupNames!![user to pkg.packageName] = newPermGroupNames

                    addSource(newPermGroupNames) {
                        pkgPermGroups[user to pkg.packageName]?.forEach { removeSource(it.value) }
                        pkgPermGroups.remove(user to pkg.packageName)

                        updateIfActive()
                    }
                }
            }
        }

        // Step 2, packages and pkgPermGroupNames are loaded, but pkgPermGroups are not loaded yet
        if (packages.isInitialized && pkgPermGroupNames != null) {
            for ((user, userPkgs) in packages.value!!) {
                for (pkg in userPkgs) {
                    if (pkgPermGroupNames!![user to pkg.packageName]?.isInitialized == true &&
                            pkgPermGroups[user to pkg.packageName] == null) {
                        pkgPermGroups[user to pkg.packageName] = mutableMapOf()

                        for (groupName in
                                pkgPermGroupNames!![user to pkg.packageName]!!.value!!.keys) {
                            if (groupName == PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS) {
                                continue
                            }

                            val newPkgPermGroup = LightAppPermGroupLiveData[pkg.packageName,
                                    groupName, user]

                            pkgPermGroups[user to pkg.packageName]!![groupName] = newPkgPermGroup

                            addSource(newPkgPermGroup) { updateIfActive() }
                        }
                    }
                }
            }
        }

        // Step 3, everything is loaded, generate data
        if (packages.isInitialized && usages.isInitialized && revokedPermGroupNames.isInitialized &&
                pkgPermGroupNames?.values?.all { it.isInitialized } == true &&
                pkgPermGroupNames?.size == pkgPermGroups.size &&
                pkgPermGroups.values.all { it?.values?.all { it.isInitialized } == true }) {
            val users = mutableListOf<AutoRevokeDumpUserData>()

            for ((user, userPkgs) in packages.value!!) {
                val pkgs = mutableListOf<AutoRevokeDumpPackageData>()

                for (pkg in userPkgs) {
                    val groups = mutableListOf<AutoRevokeDumpGroupData>()

                    for (groupName in pkgPermGroupNames!![user to pkg.packageName]!!.value!!.keys) {
                        if (groupName == PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS) {
                            continue
                        }

                        pkgPermGroups[user to pkg.packageName]!![groupName]!!.value!!.apply {
                            groups.add(AutoRevokeDumpGroupData(groupName,
                                    isBackgroundFixed || isForegroundFixed,
                                    permissions.any { (_, p) -> p.isGrantedIncludingAppOp },
                                    isGrantedByDefault,
                                    isGrantedByRole,
                                    isUserSensitive,
                                    revokedPermGroupNames.value!![pkg.packageName to user]
                                            ?.contains(groupName) ?: false
                            ))
                        }
                    }

                    pkgs.add(AutoRevokeDumpPackageData(pkg.uid, pkg.packageName,
                            pkg.firstInstallTime,
                            usages.value!![user]
                                    ?.find { it.packageName == pkg.packageName }?.lastTimeVisible,
                            groups))
                }

                users.add(AutoRevokeDumpUserData(user, pkgs))
            }

            value = AutoRevokeDumpData(users)
        }

        isUpdating = false
    }
}
+58 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.
 */
syntax = "proto2";
package com.android.permissioncontroller.permission.service;
option java_outer_classname = "AutoRevokePermissionsProto";

message PackageProto {
  optional int32 uid = 1;
  optional string package_name = 2;
  optional int64 last_time_visible = 4;
  optional int64 first_install_time = 5;
  repeated PermissionGroupProto groups = 6;
}

message PermissionGroupProto {
  optional string group_name = 1;
  optional bool is_fixed = 2;
  optional bool is_any_granted_including_appop = 3;
  optional bool is_granted_by_default = 4;
  optional bool is_granted_by_role = 5;
  optional bool is_user_sensitive = 6;
  optional bool is_auto_revoked = 7;
}

message AutoRevokedPackageProto {
  optional string package_name = 1;
  repeated string revoked_groups = 2;
}

message PerUserProto {
  optional int32 user_id = 1;
  repeated PackageProto packages = 2;
}

message TeamFoodSettingsProto {
  optional bool enabled_for_pre_R_apps = 1;
  optional int64 unused_threshold_millis = 2;
  optional int64 check_frequency_millis = 3;
}

message AutoRevokePermissionsDumpProto {
  repeated PerUserProto users = 1;

  optional TeamFoodSettingsProto teamfood_settings = 2;
}
Loading