Exempt assistant for background mic on role grant

When the device is upgraded the app which holds the assistant role
should get the background microphone permission granted if they already
hold the foreground permission.

Test: new tests
Bug: 158311343
Change-Id: Id4f7685ec1d59c9353f74c0dc84789b524589d80
diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml
index 5763b8f..cd1fca2 100644
--- a/PermissionController/res/xml/roles.xml
+++ b/PermissionController/res/xml/roles.xml
@@ -114,6 +114,9 @@
             <permission-set name="sms" />
             <permission name="android.permission.READ_CALL_LOG" />
         </permissions>
+        <exempt-permissions>
+            <permission name="android.permission.RECORD_BACKGROUND_AUDIO" />
+        </exempt-permissions>
     </role>
 
     <!---
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/RoleHoldersLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/RoleHoldersLiveData.kt
new file mode 100644
index 0000000..2cf17fb
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/RoleHoldersLiveData.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package com.android.permissioncontroller.permission.data
+
+import android.app.Application
+import android.app.role.RoleManager
+import android.os.UserHandle
+import com.android.permissioncontroller.PermissionControllerApplication
+import com.android.permissioncontroller.permission.data.RoleListenerMultiplexer.RoleHoldersChangeCallback
+import kotlinx.coroutines.Job
+
+/**
+ * LiveData for a list of packages which hold a given role for a user.
+ *
+ * @param app The current application
+ * @param roleName The name of the role
+ */
+class RoleHoldersLiveData private constructor(
+    private val app: Application,
+    private val roleName: String,
+    private val user: UserHandle
+) : SmartAsyncMediatorLiveData<List<String>>(), RoleHoldersChangeCallback {
+
+    private val roleManager = app.getSystemService(RoleManager::class.java)!!
+
+    override fun onActive() {
+        super.onActive()
+        RoleListenerMultiplexer.addCallback(roleName, user, this)
+    }
+
+    override fun onInactive() {
+        super.onInactive()
+        RoleListenerMultiplexer.removeCallback(roleName, user, this)
+    }
+
+    override suspend fun loadDataAndPostValue(job: Job) {
+        postValue(roleManager.getRoleHoldersAsUser(roleName, user))
+    }
+
+    override fun onRoleHoldersChanged() {
+        updateAsync()
+    }
+
+    /**
+     * Repository for RoleHoldersLiveData.
+     * <p> Key value is the name of the role.
+     */
+    companion object : DataRepository<Pair<String, UserHandle>, RoleHoldersLiveData>() {
+        override fun newValue(key: Pair<String, UserHandle>): RoleHoldersLiveData {
+            return RoleHoldersLiveData(PermissionControllerApplication.get(), key.first, key.second)
+        }
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/RoleListenerMultiplexer.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/RoleListenerMultiplexer.kt
new file mode 100644
index 0000000..ac85343
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/RoleListenerMultiplexer.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+package com.android.permissioncontroller.permission.data
+
+import android.app.Application
+import android.app.role.OnRoleHoldersChangedListener
+import android.app.role.RoleManager
+import android.os.UserHandle
+import androidx.annotation.GuardedBy
+import com.android.permissioncontroller.PermissionControllerApplication
+
+/**
+ * Serves as a single shared Role Change Listener.
+ */
+object RoleListenerMultiplexer : OnRoleHoldersChangedListener {
+
+    private val app: Application = PermissionControllerApplication.get()
+
+    @GuardedBy("lock")
+    private val callbacks = mutableMapOf<UserHandle,
+            MutableMap<String, MutableList<RoleHoldersChangeCallback>>>()
+
+    private val roleManager = app.getSystemService(RoleManager::class.java)!!
+
+    private val lock = Object()
+
+    override fun onRoleHoldersChanged(roleName: String, user: UserHandle) {
+        val callbacksCopy: List<RoleHoldersChangeCallback>?
+        synchronized(lock) {
+            callbacksCopy = callbacks[user]?.get(roleName)?.toList()
+        }
+        callbacksCopy?.forEach { listener ->
+            listener.onRoleHoldersChanged()
+        }
+    }
+
+    fun addCallback(roleName: String, user: UserHandle, callback: RoleHoldersChangeCallback) {
+        val wasEmpty: Boolean
+        synchronized(lock) {
+            val userCallbacks = callbacks.getOrPut(user, { mutableMapOf() })
+            wasEmpty = userCallbacks.isEmpty()
+
+            userCallbacks.getOrPut(roleName, { mutableListOf() }).add(callback)
+        }
+
+        if (wasEmpty) {
+            roleManager.addOnRoleHoldersChangedListenerAsUser(app.mainExecutor, this, user)
+        }
+    }
+
+    fun removeCallback(roleName: String, user: UserHandle, callback: RoleHoldersChangeCallback) {
+        val userCallbacksEmpty: Boolean
+        synchronized(lock) {
+            val userCallbacks = callbacks[user] ?: return
+            if (!userCallbacks.contains(roleName)) {
+                return
+            }
+
+            if (!userCallbacks[roleName]!!.remove(callback)) {
+                return
+            }
+
+            if (userCallbacks[roleName]!!.isEmpty()) {
+                userCallbacks.remove(roleName)
+            }
+
+            userCallbacksEmpty = userCallbacks.isEmpty()
+        }
+        if (userCallbacksEmpty) {
+            roleManager.removeOnRoleHoldersChangedListenerAsUser(this, user)
+        }
+    }
+
+    interface RoleHoldersChangeCallback {
+        fun onRoleHoldersChanged()
+    }
+}
\ No newline at end of file
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
index 1d4ecb4..c6b56fe 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
@@ -18,6 +18,7 @@
 
 import android.Manifest.permission
 import android.Manifest.permission_group
+import android.app.role.RoleManager
 import android.content.Context
 import android.content.pm.PackageInfo
 import android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
@@ -31,11 +32,13 @@
 import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
 import com.android.permissioncontroller.permission.data.LightPermInfoLiveData
 import com.android.permissioncontroller.permission.data.PreinstalledUserPackageInfosLiveData
+import com.android.permissioncontroller.permission.data.RoleHoldersLiveData
 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
 import com.android.permissioncontroller.permission.data.UserPackageInfosLiveData
 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.model.livedatatypes.LightPermInfo
 import com.android.permissioncontroller.permission.model.livedatatypes.LightPermission
 import com.android.permissioncontroller.permission.utils.IPC
 import com.android.permissioncontroller.permission.utils.KotlinUtils.grantBackgroundRuntimePermissions
@@ -53,7 +56,7 @@
     private val LOG_TAG = RuntimePermissionsUpgradeController::class.java.simpleName
 
     // The latest version of the runtime permissions database
-    private val LATEST_VERSION = 8
+    private val LATEST_VERSION = 9
 
     fun upgradeIfNeeded(context: Context, onComplete: Runnable) {
         val permissionManager = context.getSystemService(PermissionManager::class.java)
@@ -77,26 +80,26 @@
     }
 
     /**
-     * Create whitelistings for select permissions of select apps.
+     * Create exemptions for select restricted permissions of select apps.
      *
-     * @param permissionInfos permissions to whitelist
-     * @param pkgs packages to whitelist
+     * @param permissionInfos permissions to exempt
+     * @param pkgs packages to exempt
      *
-     * @return the whitelistings to apply
+     * @return the exemptions to apply
      */
-    private fun getWhitelistings(
+    private fun getExemptions(
         permissions: Set<String>,
         pkgs: List<LightPackageInfo>
-    ): List<Whitelisting> {
-        val whitelistings = mutableListOf<Whitelisting>()
+    ): List<RestrictionExemption> {
+        val exemptions = mutableListOf<RestrictionExemption>()
 
         for (pkg in pkgs) {
             for (permission in permissions intersect pkg.requestedPermissions) {
-                whitelistings.add(Whitelisting(pkg.packageName, permission))
+                exemptions.add(RestrictionExemption(pkg.packageName, permission))
             }
         }
 
-        return whitelistings
+        return exemptions
     }
 
     /**
@@ -126,6 +129,7 @@
 
         val needBackgroundAppPermGroups = sdkUpgradedFromP && currentVersion <= 6
         val needAccessMediaAppPermGroups = !isNewUser && currentVersion <= 7
+        val needBackgroundMicAppPermGroups = currentVersion <= 8
 
         // All data needed by this method.
         //
@@ -152,9 +156,11 @@
             /** {@link #permGroupProviders} that already provided a result */
             private val permGroupProvidersDone = mutableSetOf<LightAppPermGroupLiveData>()
 
+            /** Provides the currently set assistant  */
+            private val assistants = RoleHoldersLiveData[RoleManager.ROLE_ASSISTANT, myUserHandle()]
+
             init {
                 // First step: Load packages + perm infos
-
                 // TODO ntmyren: remove once b/154796729 is fixed
                 Log.i("RuntimePermissions", "observing UserPackageInfoLiveData for " +
                     "${myUserHandle().identifier} in RuntimePermissionsUpgradeController")
@@ -189,6 +195,14 @@
                         }
                     }
                 }
+
+                addSource(assistants) { assists ->
+                    if (assists != null) {
+                        removeSource(assistants)
+
+                        update()
+                    }
+                }
             }
 
             override fun onUpdate() {
@@ -198,7 +212,8 @@
                     permGroupProviders = mutableListOf()
 
                     // Only load app-perm-groups needed for this upgrade
-                    if (needBackgroundAppPermGroups || needAccessMediaAppPermGroups) {
+                    if (needBackgroundAppPermGroups || needAccessMediaAppPermGroups ||
+                            needBackgroundMicAppPermGroups) {
                         for ((pkgName, _, requestedPerms, requestedPermFlags) in
                                 pkgInfoProvider.value!!) {
                             var hasAccessMedia = false
@@ -222,6 +237,12 @@
                                         hasGrantedExternalStorage = true
                                     }
                                 }
+
+                                if (needBackgroundMicAppPermGroups &&
+                                        perm == permission.RECORD_BACKGROUND_AUDIO) {
+                                    permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
+                                            permission_group.MICROPHONE, myUserHandle()])
+                                }
                             }
 
                             if (hasAccessMedia && hasGrantedExternalStorage) {
@@ -251,11 +272,13 @@
                         permGroupProvidersDone.size == permGroupProviders!!.size &&
                         preinstalledPkgInfoProvider.value != null &&
                         platformRuntimePermissionInfoProviders.size
-                        == platformRuntimePermissionInfoProvidersDone.size) {
+                        == platformRuntimePermissionInfoProvidersDone.size &&
+                        assistants.value != null) {
                     // Third step: All packages, perm infos and perm groups are loaded, set value
 
                     val bgGroups = mutableListOf<LightAppPermGroup>()
                     val storageGroups = mutableListOf<LightAppPermGroup>()
+                    val bgMicGroups = mutableListOf<LightAppPermGroup>()
 
                     for (group in permGroupProviders!!.mapNotNull { it.value }) {
                         when (group.permGroupName) {
@@ -265,10 +288,16 @@
                             permission_group.STORAGE -> {
                                 storageGroups.add(group)
                             }
+                            permission_group.MICROPHONE -> {
+                                if (assistants.value!!.contains(group.packageName)) {
+                                    bgMicGroups.add(group)
+                                }
+                            }
                         }
                     }
 
-                    val restrictedPermissions = mutableSetOf<String>()
+                    val restrictedPermissions = mutableSetOf<LightPermInfo>()
+                    val restrictedPermissionsInstallerExemptIgnored = mutableSetOf<String>()
                     for (permInfoLiveDt in platformRuntimePermissionInfoProviders) {
                         val permInfo = permInfoLiveDt.value!!
 
@@ -277,11 +306,15 @@
                             continue
                         }
 
-                        restrictedPermissions.add(permInfo.name)
+                        if (permInfo.flags and PermissionInfo.FLAG_INSTALLER_EXEMPT_IGNORED != 0) {
+                            restrictedPermissionsInstallerExemptIgnored.add(permInfo.name)
+                        }
+
+                        restrictedPermissions.add(permInfo)
                     }
 
                     value = UpgradeData(preinstalledPkgInfoProvider.value!!, restrictedPermissions,
-                            pkgInfoProvider.value!!, bgGroups, storageGroups)
+                            pkgInfoProvider.value!!, bgGroups, storageGroups, bgMicGroups)
                 }
             }
         }
@@ -289,20 +322,26 @@
         // Trigger loading of data and wait until data is loaded
         val upgradeData = upgradeDataProvider.getInitializedValue(forceUpdate = true)
 
-        // Only whitelist permissions that are in the OTA. Apps that are updated via OTAs are never
-        // installed. Hence their permission are never whitelisted. This code replaces that by
-        // always whitelisting them. For non-OTA updates the installer should do the white-listing
-        val preinstalledAppWhitelistings = getWhitelistings(
-                upgradeData.restrictedPermissions,
+        // Only exempt permissions that are in the OTA. Apps that are updated via OTAs are never
+        // installed. Hence their permission are never exempted. This code replaces that by
+        // always exempting them. For non-OTA updates the installer should do the exemption.
+        // If a restricted permission can't be exempted by the installer then it should be filtered
+        // out here.
+        val preinstalledAppExemptions = getExemptions(
+                upgradeData.restrictedPermissions.filter {
+                    it.flags and PermissionInfo.FLAG_INSTALLER_EXEMPT_IGNORED == 0
+                }.map {
+                    it.name
+                }.toSet(),
                 upgradeData.preinstalledPkgs)
 
-        val (newVersion, upgradeWhitelistings, grants) = onUpgradeLockedDataLoaded(currentVersion,
-                upgradeData.pkgs, upgradeData.restrictedPermissions, upgradeData.bgGroups,
-                upgradeData.storageGroups)
+        val (newVersion, upgradeExemptions, grants) = onUpgradeLockedDataLoaded(currentVersion,
+                upgradeData.pkgs, upgradeData.restrictedPermissions.map { it.name }.toSet(),
+                upgradeData.bgGroups, upgradeData.storageGroups, upgradeData.bgMicGroups)
 
         // Do not run in parallel. Measurements have shown that this is slower than sequential
-        for (whitelisting in (preinstalledAppWhitelistings union upgradeWhitelistings)) {
-            whitelisting.applyToPlatform(context)
+        for (exemption in (preinstalledAppExemptions union upgradeExemptions)) {
+            exemption.applyToPlatform(context)
         }
 
         for (grant in grants) {
@@ -317,15 +356,16 @@
         pkgs: List<LightPackageInfo>,
         restrictedPermissions: Set<String>,
         bgApps: List<LightAppPermGroup>,
-        accessMediaApps: List<LightAppPermGroup>
-    ): Triple<Int, List<Whitelisting>, List<Grant>> {
-        val whitelistings = mutableListOf<Whitelisting>()
+        accessMediaApps: List<LightAppPermGroup>,
+        bgMicApps: List<LightAppPermGroup>
+    ): Triple<Int, List<RestrictionExemption>, List<Grant>> {
+        val exemptions = mutableListOf<RestrictionExemption>()
         val grants = mutableListOf<Grant>()
 
         var currentVersion = currVersion
         var sdkUpgradedFromP = false
         var isNewUser = false
-        val bgAppsWithWhitelisting = bgApps.map { it.packageName to it }.toMap().toMutableMap()
+        val bgAppsWithExemption = bgApps.map { it.packageName to it }.toMap().toMutableMap()
 
         if (currentVersion <= -1) {
             Log.i(LOG_TAG, "Upgrading from Android P")
@@ -347,7 +387,7 @@
                     (getPlatformPermissionNamesOfGroup(permission_group.SMS) +
                     getPlatformPermissionNamesOfGroup(permission_group.CALL_LOG))
 
-            whitelistings.addAll(getWhitelistings(permissions, pkgs))
+            exemptions.addAll(getExemptions(permissions, pkgs))
 
             currentVersion = 1
         }
@@ -365,26 +405,26 @@
         if (currentVersion == 3) {
             Log.i(LOG_TAG, "Grandfathering location background permissions")
 
-            val bgLocWhitelistings = getWhitelistings(setOf(permission.ACCESS_BACKGROUND_LOCATION),
+            val bgLocExemptions = getExemptions(setOf(permission.ACCESS_BACKGROUND_LOCATION),
                     pkgs)
 
-            // Adjust bgApps as if the whitelisting was applied
-            for ((pkgName, _) in bgLocWhitelistings) {
-                val bgApp = bgAppsWithWhitelisting[pkgName] ?: continue
+            // Adjust bgApps as if the exemption was applied
+            for ((pkgName, _) in bgLocExemptions) {
+                val bgApp = bgAppsWithExemption[pkgName] ?: continue
                 val perm = bgApp.allPermissions[permission.ACCESS_BACKGROUND_LOCATION] ?: continue
 
-                val allPermissionsWithWhitelisting = bgApp.allPermissions.toMutableMap()
-                allPermissionsWithWhitelisting[permission.ACCESS_BACKGROUND_LOCATION] =
+                val allPermissionsWithxemption = bgApp.allPermissions.toMutableMap()
+                allPermissionsWithxemption[permission.ACCESS_BACKGROUND_LOCATION] =
                         LightPermission(perm.pkgInfo, perm.permInfo, perm.isGrantedIncludingAppOp,
                         perm.flags or FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
                         perm.foregroundPerms)
 
-                bgAppsWithWhitelisting[pkgName] = LightAppPermGroup(bgApp.packageInfo,
-                        bgApp.permGroupInfo, allPermissionsWithWhitelisting,
+                bgAppsWithExemption[pkgName] = LightAppPermGroup(bgApp.packageInfo,
+                        bgApp.permGroupInfo, allPermissionsWithxemption,
                         bgApp.hasInstallToRuntimeSplit, bgApp.specialLocationGrant)
             }
 
-            whitelistings.addAll(bgLocWhitelistings)
+            exemptions.addAll(bgLocExemptions)
 
             currentVersion = 4
         }
@@ -401,8 +441,8 @@
                     getPlatformPermissionNamesOfGroup(permission_group.STORAGE)
 
             // We don't want to allow modification of storage post install, so put it
-            // on the internal system whitelist to prevent the installer changing it.
-            whitelistings.addAll(getWhitelistings(permissions, pkgs))
+            // on the internal system exemptlist to prevent the installer changing it.
+            exemptions.addAll(getExemptions(permissions, pkgs))
 
             currentVersion = 6
         }
@@ -410,7 +450,7 @@
         if (currentVersion == 6) {
             if (sdkUpgradedFromP) {
                 Log.i(LOG_TAG, "Expanding location permissions")
-                for (appPermGroup in bgAppsWithWhitelisting.values) {
+                for (appPermGroup in bgAppsWithExemption.values) {
                     if (appPermGroup.foreground.isGranted &&
                         appPermGroup.hasBackgroundGroup &&
                         !appPermGroup.background.isUserSet &&
@@ -450,9 +490,24 @@
             currentVersion = 8
         }
 
+        if (currentVersion == 8) {
+            val packagesToExempt = mutableListOf<LightPackageInfo>()
+            for (permGroup in bgMicApps) {
+                packagesToExempt.add(permGroup.packageInfo)
+                if (permGroup.foreground.isGranted) {
+                    grants.add(Grant(true, permGroup, listOf(permission.RECORD_BACKGROUND_AUDIO)))
+                }
+            }
+
+            exemptions.addAll(getExemptions(setOf(permission.RECORD_BACKGROUND_AUDIO),
+                    packagesToExempt))
+
+            currentVersion = 9
+        }
+
         // XXX: Add new upgrade steps above this point.
 
-        return Triple(currentVersion, whitelistings, grants)
+        return Triple(currentVersion, exemptions, grants)
     }
 
     /**
@@ -462,7 +517,7 @@
         /** Preinstalled packages */
         val preinstalledPkgs: List<LightPackageInfo>,
         /** Restricted permissions */
-        val restrictedPermissions: Set<String>,
+        val restrictedPermissions: Set<LightPermInfo>,
         /** Currently installed packages */
         val pkgs: List<LightPackageInfo>,
         /**
@@ -473,20 +528,25 @@
         /**
          * Storage groups that need to be inspected by {@link #onUpgradeLockedDataLoaded}
          */
-        val storageGroups: List<LightAppPermGroup>
+        val storageGroups: List<LightAppPermGroup>,
+        /**
+         * Background Microphone groups that need to be inspected by
+         * {@link #onUpgradeLockedDataLoaded}
+         */
+        val bgMicGroups: List<LightAppPermGroup>
     )
 
     /**
-     * A permission of an app that should be whitelisted
+     * A restricted permission of an app that should be exempted
      */
-    private data class Whitelisting(
-        /** Name of package to whitelist */
+    private data class RestrictionExemption(
+        /** Name of package to exempt */
         val pkgName: String,
-        /** Name of permissions to whitelist */
+        /** Name of permissions to exempt */
         val permission: String
     ) {
         /**
-         * Whitelist the permission by updating the platform state.
+         * Exempt the permission by updating the platform state.
          *
          * @param context context to use when calling the platform
          */
diff --git a/PermissionController/src/com/android/permissioncontroller/role/Role.md b/PermissionController/src/com/android/permissioncontroller/role/Role.md
index ae14025..3bc2214 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/Role.md
+++ b/PermissionController/src/com/android/permissioncontroller/role/Role.md
@@ -91,6 +91,8 @@
 - `<permissions>`: Child tags like `<permission-set>` and `<permission>` can be used to specify the
 permissions that should be granted to the app when it has the role. Several `<permission-set>` are
 defined at the beginning of `roles.xml`.
+- `<exempt-permissions>`: Child tags like `<permission-set>` and `<permission>` can be used to
+specify the restricted permissions that should be exempted for the app when it has the role.
 - `<app-op-permissions>`: The child tag `<app-op-permission>` can be used to specify the app op
 permissions whose app op should be granted to the app when it has the role.
 - `<app-ops>`: The child tag `<app-op>` can be used to specify the app ops that should be granted to
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/Permissions.java b/PermissionController/src/com/android/permissioncontroller/role/model/Permissions.java
index c1e7b13..2062743 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/Permissions.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/model/Permissions.java
@@ -536,6 +536,65 @@
         return permissionOrAppOpChanged;
     }
 
+    /**
+     * Exempts the restricted permissions
+     *
+     * @param packageName the package name of the application to exempt permissions to
+     * @param permissions the list of permissions to be exempted
+     * @param context the {@code Context} to retrieve system services
+     *
+     * @return whether any permission changed
+     */
+    public static boolean exempt(@NonNull String packageName, @NonNull List<String> permissions,
+            @NonNull Context context) {
+        PackageManager packageManager = context.getPackageManager();
+        Set<String> exemptedPermissions = new ArraySet<>(packageManager
+                .getWhitelistedRestrictedPermissions(packageName,
+                        PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM));
+        int permissionsSize = permissions.size();
+        boolean permissionChanged = false;
+        for (int i = 0; i < permissionsSize; i++) {
+            String permissionName = permissions.get(i);
+            if (!exemptedPermissions.contains(permissionName)) {
+                permissionChanged |= packageManager.addWhitelistedRestrictedPermission(
+                        packageName, permissionName,
+                        PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM);
+            }
+        }
+
+        return permissionChanged;
+    }
+
+
+    /**
+     * Restricts the restricted permissions
+     *
+     * @param packageName the package name of the application to restrict permissions to
+     * @param permissions the list of permissions to be restricted
+     * @param context the {@code Context} to retrieve system services
+     *
+     * @return whether any permission changed
+     */
+    public static boolean restrict(@NonNull String packageName, @NonNull List<String> permissions,
+            @NonNull Context context) {
+        PackageManager packageManager = context.getPackageManager();
+        Set<String> exemptedPermissions = new ArraySet<>(packageManager
+                .getWhitelistedRestrictedPermissions(packageName,
+                        PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM));
+        int permissionsSize = permissions.size();
+        boolean permissionChanged = false;
+        for (int i = 0; i < permissionsSize; i++) {
+            String permissionName = permissions.get(i);
+            if (exemptedPermissions.contains(permissionName)) {
+                permissionChanged |= packageManager.removeWhitelistedRestrictedPermission(
+                        packageName, permissionName,
+                        PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM);
+            }
+        }
+
+        return permissionChanged;
+    }
+
     @Nullable
     private static PackageInfo getPackageInfo(@NonNull String packageName,
             @NonNull Context context) {
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/Role.java b/PermissionController/src/com/android/permissioncontroller/role/model/Role.java
index 50bcb7c..6652199 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/Role.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/model/Role.java
@@ -173,6 +173,12 @@
     private final List<String> mPermissions;
 
     /**
+     * Restricted permissions to be exempted by this role
+     */
+    @NonNull
+    private final List<String> mExemptedPermissions;
+
+    /**
      * The app op permissions to be granted by this role.
      */
     @NonNull
@@ -197,8 +203,9 @@
             boolean requestable, @StringRes int searchKeywordsResource,
             @StringRes int shortLabelResource, boolean showNone, boolean systemOnly,
             boolean visible, @NonNull List<RequiredComponent> requiredComponents,
-            @NonNull List<String> permissions, @NonNull List<String> appOpPermissions,
-            @NonNull List<AppOp> appOps, @NonNull List<PreferredActivity> preferredActivities) {
+            @NonNull List<String> permissions, @NonNull List<String> exemptedPermissions,
+            @NonNull List<String> appOpPermissions, @NonNull List<AppOp> appOps,
+            @NonNull List<PreferredActivity> preferredActivities) {
         mName = name;
         mBehavior = behavior;
         mDefaultHoldersResourceName = defaultHoldersResourceName;
@@ -216,6 +223,7 @@
         mVisible = visible;
         mRequiredComponents = requiredComponents;
         mPermissions = permissions;
+        mExemptedPermissions = exemptedPermissions;
         mAppOpPermissions = appOpPermissions;
         mAppOps = appOps;
         mPreferredActivities = preferredActivities;
@@ -692,6 +700,7 @@
             boolean overrideUserSetAndFixedPermissions, @NonNull Context context) {
         boolean permissionOrAppOpChanged = Permissions.grant(packageName, mPermissions, true,
                 overrideUserSetAndFixedPermissions, true, false, false, context);
+        permissionOrAppOpChanged |= Permissions.exempt(packageName, mExemptedPermissions, context);
 
         int appOpPermissionsSize = mAppOpPermissions.size();
         for (int i = 0; i < appOpPermissionsSize; i++) {
@@ -736,15 +745,20 @@
         otherRoleNames.remove(mName);
 
         List<String> permissionsToRevoke = new ArrayList<>(mPermissions);
+        List<String> permissionsToRestrict = new ArrayList<>(mExemptedPermissions);
         ArrayMap<String, Role> roles = Roles.get(context);
         int otherRoleNamesSize = otherRoleNames.size();
         for (int i = 0; i < otherRoleNamesSize; i++) {
             String roleName = otherRoleNames.get(i);
             Role role = roles.get(roleName);
             permissionsToRevoke.removeAll(role.mPermissions);
+            permissionsToRestrict.removeAll(role.mExemptedPermissions);
         }
+
         boolean permissionOrAppOpChanged = Permissions.revoke(packageName, permissionsToRevoke,
                 true, false, overrideSystemFixedPermissions, context);
+        permissionOrAppOpChanged |=
+                Permissions.restrict(packageName, permissionsToRestrict, context);
 
         List<String> appOpPermissionsToRevoke = new ArrayList<>(mAppOpPermissions);
         for (int i = 0; i < otherRoleNamesSize; i++) {
@@ -885,6 +899,7 @@
                 + ", mVisible=" + mVisible
                 + ", mRequiredComponents=" + mRequiredComponents
                 + ", mPermissions=" + mPermissions
+                + ", mExemptedPermissions=" + mExemptedPermissions
                 + ", mAppOpPermissions=" + mAppOpPermissions
                 + ", mAppOps=" + mAppOps
                 + ", mPreferredActivities=" + mPreferredActivities
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RoleParser.java b/PermissionController/src/com/android/permissioncontroller/role/model/RoleParser.java
index 2678ff9..52b8be4 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/RoleParser.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/model/RoleParser.java
@@ -64,6 +64,7 @@
     private static final String TAG_CATEGORY = "category";
     private static final String TAG_DATA = "data";
     private static final String TAG_PERMISSIONS = "permissions";
+    private static final String TAG_EXEMPT_PERMISSIONS = "exempt-permissions";
     private static final String TAG_APP_OP_PERMISSIONS = "app-op-permissions";
     private static final String TAG_APP_OP_PERMISSION = "app-op-permission";
     private static final String TAG_APP_OPS = "app-ops";
@@ -363,6 +364,7 @@
 
         List<RequiredComponent> requiredComponents = null;
         List<String> permissions = null;
+        List<String> exemptedPermissions = null;
         List<String> appOpPermissions = null;
         List<AppOp> appOps = null;
         List<PreferredActivity> preferredActivities = null;
@@ -394,6 +396,14 @@
                     }
                     permissions = parsePermissions(parser, permissionSets);
                     break;
+                case TAG_EXEMPT_PERMISSIONS:
+                    if (exemptedPermissions != null) {
+                        throwOrLogMessage("Duplicate <exempt-permissions> in role: " + name);
+                        skipCurrentTag(parser);
+                        continue;
+                    }
+                    exemptedPermissions = parsePermissions(parser, permissionSets);
+                    break;
                 case TAG_APP_OP_PERMISSIONS:
                     if (appOpPermissions != null) {
                         throwOrLogMessage("Duplicate <app-op-permissions> in role: " + name);
@@ -430,6 +440,9 @@
         if (permissions == null) {
             permissions = Collections.emptyList();
         }
+        if (exemptedPermissions == null) {
+            exemptedPermissions = Collections.emptyList();
+        }
         if (appOpPermissions == null) {
             appOpPermissions = Collections.emptyList();
         }
@@ -442,8 +455,8 @@
         return new Role(name, behavior, defaultHoldersResourceName, descriptionResource, exclusive,
                 fallBackToDefaultHolder, labelResource, requestDescriptionResource,
                 requestTitleResource, requestable, searchKeywordsResource, shortLabelResource,
-                showNone, systemOnly, visible, requiredComponents, permissions, appOpPermissions,
-                appOps, preferredActivities);
+                showNone, systemOnly, visible, requiredComponents, permissions, exemptedPermissions,
+                appOpPermissions, appOps, preferredActivities);
     }
 
     @NonNull
diff --git a/PermissionController/tests/hostside/upgrade/Android.bp b/PermissionController/tests/hostside/upgrade/Android.bp
index 08aa126..3055127 100644
--- a/PermissionController/tests/hostside/upgrade/Android.bp
+++ b/PermissionController/tests/hostside/upgrade/Android.bp
@@ -35,5 +35,6 @@
         ":PermissionUpgradeCurrentHasCameraDenied",
         ":PermissionUpgradeCurrentHasCameraGrantedAndDeclaresBgCamera",
         ":PermissionUpgradeCurrentHasCameraDeniedAndDeclaresBgCamera",
+        ":PermissionUpgrade30Assistant",
     ],
 }
diff --git a/PermissionController/tests/hostside/upgrade/AndroidTest.xml b/PermissionController/tests/hostside/upgrade/AndroidTest.xml
index f8e3a00..f3ae6f2 100644
--- a/PermissionController/tests/hostside/upgrade/AndroidTest.xml
+++ b/PermissionController/tests/hostside/upgrade/AndroidTest.xml
@@ -33,5 +33,6 @@
         <option name="test-file-name" value="PermissionUpgradeCurrentHasCameraDenied.apk" />
         <option name="test-file-name" value="PermissionUpgradeCurrentHasCameraGrantedAndDeclaresBgCamera.apk" />
         <option name="test-file-name" value="PermissionUpgradeCurrentHasCameraDeniedAndDeclaresBgCamera.apk" />
+        <option name="test-file-name" value="PermissionUpgrade30Assistant.apk" />
     </target_preparer>
-</configuration>
\ No newline at end of file
+</configuration>
diff --git a/PermissionController/tests/hostside/upgrade/res/raw/roles-30.xml b/PermissionController/tests/hostside/upgrade/res/raw/roles-30.xml
new file mode 100644
index 0000000..d5d0efe
--- /dev/null
+++ b/PermissionController/tests/hostside/upgrade/res/raw/roles-30.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<roles version="-1" packagesHash="">
+    <role name="android.app.role.ASSISTANT">
+        <holder name="com.android.permission.test.upgrade.sdk_30.assistant" />
+    </role>
+</roles>
\ No newline at end of file
diff --git a/PermissionController/tests/hostside/upgrade/res/raw/runtime-permissions-30.xml b/PermissionController/tests/hostside/upgrade/res/raw/runtime-permissions-30_AssistantMicDenied.xml
similarity index 89%
copy from PermissionController/tests/hostside/upgrade/res/raw/runtime-permissions-30.xml
copy to PermissionController/tests/hostside/upgrade/res/raw/runtime-permissions-30_AssistantMicDenied.xml
index 8db4adb..66d3409 100644
--- a/PermissionController/tests/hostside/upgrade/res/raw/runtime-permissions-30.xml
+++ b/PermissionController/tests/hostside/upgrade/res/raw/runtime-permissions-30_AssistantMicDenied.xml
@@ -24,4 +24,7 @@
   <package name="com.android.permission.test.upgrade.sdk_current.camera_denied_declares_bg">

     <permission name="android.permission.CAMERA" granted="false" flags="300" />

   </package>

+  <package name="com.android.permission.test.upgrade.sdk_30.assistant">

+    <permission name="android.permission.RECORD_AUDIO" granted="false" flags="300" />

+  </package>

 </runtime-permissions>

diff --git a/PermissionController/tests/hostside/upgrade/res/raw/runtime-permissions-30.xml b/PermissionController/tests/hostside/upgrade/res/raw/runtime-permissions-30_AssistantMicGranted.xml
similarity index 89%
rename from PermissionController/tests/hostside/upgrade/res/raw/runtime-permissions-30.xml
rename to PermissionController/tests/hostside/upgrade/res/raw/runtime-permissions-30_AssistantMicGranted.xml
index 8db4adb..fe7367f 100644
--- a/PermissionController/tests/hostside/upgrade/res/raw/runtime-permissions-30.xml
+++ b/PermissionController/tests/hostside/upgrade/res/raw/runtime-permissions-30_AssistantMicGranted.xml
@@ -24,4 +24,7 @@
   <package name="com.android.permission.test.upgrade.sdk_current.camera_denied_declares_bg">

     <permission name="android.permission.CAMERA" granted="false" flags="300" />

   </package>

+  <package name="com.android.permission.test.upgrade.sdk_30.assistant">

+    <permission name="android.permission.RECORD_AUDIO" granted="true" flags="300" />

+  </package>

 </runtime-permissions>

diff --git a/PermissionController/tests/hostside/upgrade/src/com/android/permissioncontroller/test/upgrade/Upgrade30ToCurrent.kt b/PermissionController/tests/hostside/upgrade/src/com/android/permissioncontroller/test/upgrade/Upgrade30ToCurrent.kt
index ea77852..3ee2149 100644
--- a/PermissionController/tests/hostside/upgrade/src/com/android/permissioncontroller/test/upgrade/Upgrade30ToCurrent.kt
+++ b/PermissionController/tests/hostside/upgrade/src/com/android/permissioncontroller/test/upgrade/Upgrade30ToCurrent.kt
@@ -28,33 +28,58 @@
 class Upgrade30ToCurrent : BaseHostJUnit4Test() {
 
     companion object {
-        private const val FULL_REBOOT = true
-        private const val PERMISSION_SDK30_PATH = "raw/runtime-permissions-30.xml"
+        private const val FULL_REBOOT = false
+        private const val PERMISSION_SDK30_ASSISTANT_MIC_GRANTED_PATH =
+                "raw/runtime-permissions-30_AssistantMicGranted.xml"
+        private const val PERMISSION_SDK30_ASSISTANT_MIC_DENIED_PATH =
+                "raw/runtime-permissions-30_AssistantMicDenied.xml"
+        private const val PERMISSION_SDK30_ROLES_PATH = "raw/roles-30.xml"
+
         private const val SDK30_DEVICE_PERMISSION_DB_PATH =
                 "/data/misc_de/0/apexdata/com.android.permission/runtime-permissions.xml"
         private const val CURRENT_DEVICE_PERMISSION_DB_PATH =
                 "/data/misc_de/0/apexdata/com.android.permission/runtime-permissions.xml"
+
+        private const val SDK30_DEVICE_ROLES_DB_PATH =
+                "/data/misc_de/0/apexdata/com.android.permission/roles.xml"
+        private const val CURRENT_DEVICE_ROLES_DB_PATH =
+                "/data/misc_de/0/apexdata/com.android.permission/roles.xml"
     }
 
     @Test
     fun testUpgrade1() {
-        simulateUpgrade(PERMISSION_SDK30_PATH)
+        simulateUpgrade(PERMISSION_SDK30_ASSISTANT_MIC_GRANTED_PATH, PERMISSION_SDK30_ROLES_PATH)
+
         runDeviceSideTest("testUpgrade1")
     }
 
-    fun simulateUpgrade(permissionsPath: String) {
+    @Test
+    fun testUpgrade2() {
+        simulateUpgrade(PERMISSION_SDK30_ASSISTANT_MIC_DENIED_PATH, PERMISSION_SDK30_ROLES_PATH)
+
+        runDeviceSideTest("testUpgrade2")
+    }
+
+    fun simulateUpgrade(permissionsPath: String, rolesPath: String) {
         runDeviceSideTest("verifyPackagesAreInstalled")
         device.executeShellCommand("stop")
 
         // Delete the current permissions file and write the R version
         device.deleteFile(CURRENT_DEVICE_PERMISSION_DB_PATH)
+        device.deleteFile(CURRENT_DEVICE_ROLES_DB_PATH)
         val tmpFolder = TemporaryFolder()
         tmpFolder.create()
-        val tmpFile = File(tmpFolder.root, "runtime-permissions.xml")
-        val input = javaClass.classLoader!!.getResourceAsStream(permissionsPath)
+
+        var tmpFile = File(tmpFolder.root, "runtime-permissions.xml")
+        var input = javaClass.classLoader!!.getResourceAsStream(permissionsPath)
         Files.copy(input, tmpFile.toPath())
         device.pushFile(tmpFile, SDK30_DEVICE_PERMISSION_DB_PATH)
 
+        tmpFile = File(tmpFolder.root, "roles.xml")
+        input = javaClass.classLoader!!.getResourceAsStream(rolesPath)
+        Files.copy(input, tmpFile.toPath())
+        device.pushFile(tmpFile, SDK30_DEVICE_ROLES_DB_PATH)
+
         if (FULL_REBOOT) {
             device.reboot()
         } else {
diff --git a/PermissionController/tests/hostside/upgrade/test-apps/Android.bp b/PermissionController/tests/hostside/upgrade/test-apps/Android.bp
index 7900d83..06e01fd 100644
--- a/PermissionController/tests/hostside/upgrade/test-apps/Android.bp
+++ b/PermissionController/tests/hostside/upgrade/test-apps/Android.bp
@@ -54,4 +54,10 @@
 android_test_helper_app {
     name: "PermissionUpgradeCurrentHasCameraDeniedAndDeclaresBgCamera",
     manifest: "PermissionUpgradeCurrentHasCameraDeniedAndDeclaresBgCamera.xml"
-}
\ No newline at end of file
+}
+
+android_test_helper_app {
+    name: "PermissionUpgrade30Assistant",
+    manifest: "PermissionUpgrade30Assistant.xml",
+    sdk_version: "30",
+}
diff --git a/PermissionController/tests/hostside/upgrade/test-apps/DeviceSide/src/com/android/permissioncontroller/test/upgrade/deviceside/DeviceSide.kt b/PermissionController/tests/hostside/upgrade/test-apps/DeviceSide/src/com/android/permissioncontroller/test/upgrade/deviceside/DeviceSide.kt
index 0ad7613..aeace23 100644
--- a/PermissionController/tests/hostside/upgrade/test-apps/DeviceSide/src/com/android/permissioncontroller/test/upgrade/deviceside/DeviceSide.kt
+++ b/PermissionController/tests/hostside/upgrade/test-apps/DeviceSide/src/com/android/permissioncontroller/test/upgrade/deviceside/DeviceSide.kt
@@ -18,6 +18,8 @@
 
 import android.Manifest.permission.BACKGROUND_CAMERA
 import android.Manifest.permission.CAMERA
+import android.Manifest.permission.RECORD_AUDIO
+import android.Manifest.permission.RECORD_BACKGROUND_AUDIO
 import android.app.UiAutomation
 import android.content.Context
 import android.content.pm.PackageManager
@@ -60,6 +62,9 @@
         private const val PKG_CURRENT_CAMERA_DENIED_DECLARES_BG =
                 "com.android.permission.test.upgrade.sdk_current.camera_denied_declares_bg"
 
+        private const val PKG_30_ASSISTANT =
+                "com.android.permission.test.upgrade.sdk_30.assistant"
+
         private const val ALL_PERMISSION_EXEMPT_FLAGS =
                 FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT or
                         FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT or
@@ -256,6 +261,56 @@
                 }
             }
 
+            runTest("testGetsBgMicGranted_sdk30AssistantMicGranted") {
+                assertThat(pm.checkPermission(RECORD_AUDIO, PKG_30_ASSISTANT))
+                        .isEqualTo(PERMISSION_GRANTED)
+                assertThat(pm
+                        .checkPermission(RECORD_BACKGROUND_AUDIO, PKG_30_ASSISTANT))
+                        .isEqualTo(PERMISSION_GRANTED)
+            }
+            runTest("testBgMicRestrictionNotApplied_sdk30AssistantMicGranted") {
+                runWithShellPermissionIdentity {
+                    assertThat(pm.getPermissionFlags(RECORD_BACKGROUND_AUDIO,
+                            PKG_30_ASSISTANT, user) and
+                            FLAG_PERMISSION_APPLY_RESTRICTION).isEqualTo(0)
+                }
+            }
+            runTest("testBgMicIsExempt_sdk30AssistantMicGranted") {
+                runWithShellPermissionIdentity {
+                    assertThat(pm.getPermissionFlags(RECORD_BACKGROUND_AUDIO,
+                            PKG_30_ASSISTANT, user) and
+                            ALL_PERMISSION_EXEMPT_FLAGS).isNotEqualTo(0)
+                }
+            }
+
+            finish()
+        }
+    }
+
+    @Test
+    fun testUpgrade2() {
+        with(MultipleTestRunner()) {
+            runTest("testGetsBgMicGranted_sdk30AssistantMicDenied") {
+                assertThat(pm.checkPermission(RECORD_AUDIO, PKG_30_ASSISTANT))
+                        .isEqualTo(PERMISSION_DENIED)
+                assertThat(pm.checkPermission(RECORD_BACKGROUND_AUDIO, PKG_30_ASSISTANT))
+                        .isEqualTo(PERMISSION_DENIED)
+            }
+            runTest("testBgMicRestrictionNotApplied_sdk30AssistantMicDenied") {
+                runWithShellPermissionIdentity {
+                    assertThat(pm.getPermissionFlags(RECORD_BACKGROUND_AUDIO,
+                            PKG_30_ASSISTANT, user) and
+                            FLAG_PERMISSION_APPLY_RESTRICTION).isEqualTo(0)
+                }
+            }
+            runTest("testBgMicIsExempt_sdk30AssistantMicDenied") {
+                runWithShellPermissionIdentity {
+                    assertThat(pm.getPermissionFlags(RECORD_BACKGROUND_AUDIO,
+                            PKG_30_ASSISTANT, user) and
+                            ALL_PERMISSION_EXEMPT_FLAGS).isNotEqualTo(0)
+                }
+            }
+
             finish()
         }
     }
diff --git a/PermissionController/tests/hostside/upgrade/test-apps/PermissionUpgrade30Assistant.xml b/PermissionController/tests/hostside/upgrade/test-apps/PermissionUpgrade30Assistant.xml
new file mode 100644
index 0000000..9afb679
--- /dev/null
+++ b/PermissionController/tests/hostside/upgrade/test-apps/PermissionUpgrade30Assistant.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.permission.test.upgrade.sdk_30.assistant">
+
+    <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+
+    <application>
+        <activity
+            android:name=".AssistantActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.ASSIST" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeControllerTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeControllerTest.kt
index e9af152..195b5e2 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeControllerTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeControllerTest.kt
@@ -21,15 +21,19 @@
 import android.Manifest.permission.ACCESS_MEDIA_LOCATION
 import android.Manifest.permission.READ_CALL_LOG
 import android.Manifest.permission.READ_EXTERNAL_STORAGE
+import android.Manifest.permission.RECORD_AUDIO
+import android.Manifest.permission.RECORD_BACKGROUND_AUDIO
 import android.Manifest.permission.SEND_SMS
 import android.app.ActivityManager
 import android.app.AppOpsManager
 import android.app.job.JobScheduler
+import android.app.role.RoleManager
 import android.content.ComponentCallbacks2
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageInfo
 import android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED
 import android.content.pm.PackageManager
+import android.content.pm.PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE
 import android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT
 import android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
 import android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
@@ -83,7 +87,7 @@
     }
 
     /** Latest permission database version known in this test */
-    private val LATEST_VERSION = 8;
+    private val LATEST_VERSION = 9
 
     /** Use a unique test package name for each test */
     private val TEST_PKG_NAME: String
@@ -107,6 +111,8 @@
     lateinit var userManager: UserManager
     @Mock
     lateinit var jobScheduler: JobScheduler
+    @Mock
+    lateinit var roleManager: RoleManager
 
     /**
      * Set up {@link #packageManager} as if the passed packages are installed.
@@ -154,6 +160,15 @@
         }
     }
 
+    private fun setRoleHolders(roleName: String, pkgs: List<Package>) {
+        whenever(roleManager.getRoleHolders(eq(roleName))).thenAnswer {
+            pkgs.map { it.name }
+        }
+        whenever(roleManager.getRoleHoldersAsUser(eq(roleName), any())).thenAnswer {
+            pkgs.map { it.name }
+        }
+    }
+
     /**
      * Set up system, i.e. point all the services to the mocks and forward some boring methods to
      * the system.
@@ -178,6 +193,7 @@
                 userManager)
         whenever(application.getSystemService(JobScheduler::class.java)).thenReturn(
                 jobScheduler)
+        whenever(application.getSystemService(RoleManager::class.java)).thenReturn(roleManager)
 
         whenever(application.packageManager).thenReturn(packageManager)
 
@@ -214,10 +230,14 @@
         whenever(permissionManager.runtimePermissionsVersion).thenReturn(initialVersion)
     }
 
-    private fun verifyWhitelisted(packageName: String, vararg permissionNames: String) {
+    private fun verifyWhitelisted(
+        packageName: String,
+        vararg permissionNames: String,
+        flag: Int = FLAG_PERMISSION_WHITELIST_UPGRADE
+    ) {
         for (permissionName in permissionNames) {
             verify(packageManager, timeout(100)).addWhitelistedRestrictedPermission(
-                    packageName, permissionName, FLAG_PERMISSION_WHITELIST_UPGRADE)
+                    packageName, permissionName, flag)
         }
     }
 
@@ -479,6 +499,35 @@
         verifyNotGranted(TEST_PKG_NAME, ACCESS_MEDIA_LOCATION)
     }
 
+    @Test
+    fun microphoneDoesNotGetExpandedWhenVersionIs8() {
+        setInitialDatabaseVersion(8)
+        val pkg = Package(TEST_PKG_NAME,
+                Permission(RECORD_AUDIO, isGranted = true),
+                Permission(RECORD_BACKGROUND_AUDIO))
+        setPackages(pkg)
+
+        upgradeIfNeeded()
+
+        verifyNotWhitelisted(TEST_PKG_NAME, RECORD_BACKGROUND_AUDIO)
+        verifyNotGranted(TEST_PKG_NAME, RECORD_BACKGROUND_AUDIO)
+    }
+
+    @Test
+    fun microphoneGetsExemptedForAssistantWhenVersionIs8() {
+        setInitialDatabaseVersion(8)
+        val pkg = Package(TEST_PKG_NAME,
+                Permission(RECORD_AUDIO, isGranted = true),
+                Permission(RECORD_BACKGROUND_AUDIO))
+        setPackages(pkg)
+        setRoleHolders(RoleManager.ROLE_ASSISTANT, listOf(pkg))
+
+        upgradeIfNeeded()
+
+        verifyWhitelisted(TEST_PKG_NAME, RECORD_BACKGROUND_AUDIO,
+                flag = FLAG_PERMISSION_ALLOWLIST_ROLE)
+    }
+
     @After
     fun resetSystem() {
         // Send low memory notifications for all data repositories which will clear cached data