device aware self revoke and one time session timeout

Bug: 283978092
Test: atest RevokeSelfPermissionTest
Test: atest OneTimePermissionTest
Test: atest DevicePermissionsTest

Change-Id: I0adef572133a288c17e43b3e1b8adcf53ff68114
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7c78f09..69ed5ae 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10635,12 +10635,14 @@
     method @BinderThread public abstract void onGetRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.OutputStream, @NonNull Runnable);
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void onGetUnusedAppCount(@NonNull java.util.function.IntConsumer);
     method @BinderThread public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable);
-    method @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
+    method @Deprecated @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
+    method @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String, int);
     method @Deprecated @BinderThread public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
     method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable);
     method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
-    method @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
+    method @Deprecated @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
+    method @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, int, @NonNull Runnable);
     method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @BinderThread public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull android.permission.AdminPermissionControlParams, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index e3f02e7..1cb7930 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -43,7 +43,7 @@
     void setRuntimePermissionGrantStateByDeviceAdminFromParams(String callerPackageName,
             in AdminPermissionControlParams params, in AndroidFuture callback);
     void grantOrUpgradeDefaultRuntimePermissions(in AndroidFuture callback);
-    void notifyOneTimePermissionSessionTimeout(String packageName);
+    void notifyOneTimePermissionSessionTimeout(String packageName, int deviceId);
     void updateUserSensitiveForApp(int uid, in AndroidFuture callback);
     void getPrivilegesDescriptionStringForProfile(
             in String deviceProfileName,
@@ -60,5 +60,5 @@
                 in String packageName,
                 in AndroidFuture callback);
     void revokeSelfPermissionsOnKill(in String packageName, in List<String> permissions,
-            in AndroidFuture callback);
+        int deviceId, in AndroidFuture callback);
 }
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 18ede44d..7cecfdc 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -78,7 +78,7 @@
     List<SplitPermissionInfoParcelable> getSplitPermissions();
 
     @EnforcePermission("MANAGE_ONE_TIME_PERMISSION_SESSIONS")
-    void startOneTimePermissionSession(String packageName, int userId, long timeout,
+    void startOneTimePermissionSession(String packageName, int deviceId, int userId, long timeout,
             long revokeAfterKilledDelay);
 
     @EnforcePermission("MANAGE_ONE_TIME_PERMISSION_SESSIONS")
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 84a197a..ba75918 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -764,13 +764,14 @@
      * inactive.
      *
      * @param packageName The package which became inactive
-     *
+     * @param deviceId The device ID refers either the primary device i.e. the phone or
+     *                 a virtual device. See {@link Context#DEVICE_ID_DEFAULT}
      * @hide
      */
     @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
-    public void notifyOneTimePermissionSessionTimeout(@NonNull String packageName) {
-        mRemoteService.run(
-                service -> service.notifyOneTimePermissionSessionTimeout(packageName));
+    public void notifyOneTimePermissionSessionTimeout(@NonNull String packageName, int deviceId) {
+        mRemoteService.run(service -> service.notifyOneTimePermissionSessionTimeout(
+                packageName, deviceId));
     }
 
     /**
@@ -930,12 +931,14 @@
             @NonNull List<String> permissions) {
         mRemoteService.postAsync(service -> {
             AndroidFuture<Void> callback = new AndroidFuture<>();
-            service.revokeSelfPermissionsOnKill(packageName, permissions, callback);
+            service.revokeSelfPermissionsOnKill(packageName, permissions, mContext.getDeviceId(),
+                    callback);
             return callback;
         }).whenComplete((result, err) -> {
             if (err != null) {
                 Log.e(TAG, "Failed to self revoke " + String.join(",", permissions)
-                        + " for package " + packageName, err);
+                        + " for package " + packageName + ", and device " + mContext.getDeviceId(),
+                        err);
             }
         });
     }
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 11005a6..9fe2599 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -30,6 +30,7 @@
 
 import android.Manifest;
 import android.annotation.BinderThread;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
@@ -37,6 +38,7 @@
 import android.app.admin.DevicePolicyManager.PermissionGrantState;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.Disabled;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -46,6 +48,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.UserHandle;
 import android.permission.PermissionControllerManager.CountPermissionAppsFlag;
+import android.permission.flags.Flags;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -296,13 +299,32 @@
      * This method is called at the end of a one-time permission session
      *
      * @param packageName The package that has been inactive
+     *
+     * @deprecated Implement {@link #onOneTimePermissionSessionTimeout(String, int)} instead.
      */
+    @Deprecated
     @BinderThread
     public void onOneTimePermissionSessionTimeout(@NonNull String packageName) {
         throw new AbstractMethodError("Must be overridden in implementing class");
     }
 
     /**
+     * Called when a package is considered inactive based on the criteria given by
+     * {@link PermissionManager#startOneTimePermissionSession(String, long, long, int, int)}.
+     * This method is called at the end of a one-time permission session
+     *
+     * @param packageName The package that has been inactive
+     * @param deviceId The device ID refers either the primary device i.e. the phone or
+     *                 a virtual device. See {@link Context#DEVICE_ID_DEFAULT}
+     */
+    @BinderThread
+    @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
+    public void onOneTimePermissionSessionTimeout(@NonNull String packageName,
+            int deviceId) {
+        onOneTimePermissionSessionTimeout(packageName);
+    }
+
+    /**
      * Get the platform permissions which belong to a particular permission group
      *
      * @param permissionGroupName The permission group whose permissions are desired
@@ -341,13 +363,42 @@
      * @param callback Callback waiting for operation to be complete.
      *
      * @see android.content.Context#revokeSelfPermissionsOnKill(java.util.Collection)
+     *
+     * @deprecated Implement {@link #onRevokeSelfPermissionsOnKill(String, List, int, Runnable)}
+     * instead.
      */
+    @Deprecated
     @BinderThread
     public void onRevokeSelfPermissionsOnKill(@NonNull String packageName,
             @NonNull List<String> permissions, @NonNull Runnable callback) {
         throw new AbstractMethodError("Must be overridden in implementing class");
     }
 
+    /**
+     * Triggers the revocation of one or more permissions for a package and device.
+     * This should only be called at the request of {@code packageName}.
+     * <p>
+     * Background permissions which have no corresponding foreground permission still granted once
+     * the revocation is effective will also be revoked.
+     * <p>
+     * This revocation happens asynchronously and kills all processes running in the same UID as
+     * {@code packageName}. It will be triggered once it is safe to do so.
+     *
+     * @param packageName The name of the package for which the permissions will be revoked.
+     * @param permissions List of permissions to be revoked.
+     * @param deviceId The device ID refers either the primary device i.e. the phone or
+     *                 a virtual device. See {@link Context#DEVICE_ID_DEFAULT}
+     * @param callback Callback waiting for operation to be complete.
+     *
+     * @see android.content.Context#revokeSelfPermissionsOnKill(java.util.Collection)
+     */
+    @BinderThread
+    @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
+    public void onRevokeSelfPermissionsOnKill(@NonNull String packageName,
+            @NonNull List<String> permissions, int deviceId, @NonNull Runnable callback) {
+        onRevokeSelfPermissionsOnKill(packageName, permissions, callback);
+    }
+
     // TODO(b/272129940): Remove this API and device profile role description when we drop T
     //  support.
     /**
@@ -613,12 +664,12 @@
             }
 
             @Override
-            public void notifyOneTimePermissionSessionTimeout(String packageName) {
+            public void notifyOneTimePermissionSessionTimeout(String packageName, int deviceId) {
                 enforceSomePermissionsGrantedToCaller(
                         Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
                 packageName = Preconditions.checkNotNull(packageName,
                         "packageName cannot be null");
-                onOneTimePermissionSessionTimeout(packageName);
+                onOneTimePermissionSessionTimeout(packageName, deviceId);
             }
 
             @Override
@@ -710,7 +761,8 @@
 
             @Override
             public void revokeSelfPermissionsOnKill(@NonNull String packageName,
-                    @NonNull List<String> permissions, @NonNull AndroidFuture callback) {
+                    @NonNull List<String> permissions, int deviceId,
+                    @NonNull AndroidFuture callback) {
                 try {
                     Objects.requireNonNull(callback);
 
@@ -721,7 +773,7 @@
                         enforceSomePermissionsGrantedToCaller(
                                 Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
                     }
-                    onRevokeSelfPermissionsOnKill(packageName, permissions,
+                    onRevokeSelfPermissionsOnKill(packageName, permissions, deviceId,
                             () -> callback.complete(null));
                 } catch (Throwable t) {
                     callback.completeExceptionally(t);
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 7d39210..e10ea10 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -214,6 +214,12 @@
     public static final boolean DEBUG_TRACE_PERMISSION_UPDATES = false;
 
     /**
+     * Additional debug log for virtual device permissions.
+     * @hide
+     */
+    public static final boolean DEBUG_DEVICE_PERMISSIONS = false;
+
+    /**
      * Intent extra: List of PermissionGroupUsages
      * <p>
      * Type: {@code List<PermissionGroupUsage>}
@@ -1392,8 +1398,8 @@
             @ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer,
             @ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) {
         try {
-            mPermissionManager.startOneTimePermissionSession(packageName, mContext.getUserId(),
-                    timeoutMillis, revokeAfterKilledDelayMillis);
+            mPermissionManager.startOneTimePermissionSession(packageName, mContext.getDeviceId(),
+                    mContext.getUserId(), timeoutMillis, revokeAfterKilledDelayMillis);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 15620b7..11ae9c3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -206,6 +206,7 @@
     <uses-permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY" />
     <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
     <uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
+    <uses-permission android:name="android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS" />
     <uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
     <uses-permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS" />
     <!-- Permission required for processes that don't own the focused window to switch
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
index 3296c1f..150ca9b 100644
--- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -91,13 +91,14 @@
         mHandler = context.getMainThreadHandler();
     }
 
-    void startPackageOneTimeSession(@NonNull String packageName, long timeoutMillis,
+    void startPackageOneTimeSession(@NonNull String packageName, int deviceId, long timeoutMillis,
             long revokeAfterKilledDelayMillis) {
         int uid;
         try {
             uid = mContext.getPackageManager().getPackageUid(packageName, 0);
         } catch (PackageManager.NameNotFoundException e) {
-            Log.e(LOG_TAG, "Unknown package name " + packageName, e);
+            Log.e(LOG_TAG,
+                    "Unknown package name " + packageName + ", device ID " + deviceId, e);
             return;
         }
 
@@ -107,7 +108,7 @@
                 listener.updateSessionParameters(timeoutMillis, revokeAfterKilledDelayMillis);
                 return;
             }
-            listener = new PackageInactivityListener(uid, packageName, timeoutMillis,
+            listener = new PackageInactivityListener(uid, packageName, deviceId, timeoutMillis,
                     revokeAfterKilledDelayMillis);
             mListeners.put(uid, listener);
         }
@@ -159,6 +160,7 @@
 
         private final int mUid;
         private final @NonNull String mPackageName;
+        private final int mDeviceId;
         private long mTimeout;
         private long mRevokeAfterKilledDelay;
 
@@ -191,14 +193,15 @@
             }
         };
 
-        private PackageInactivityListener(int uid, @NonNull String packageName, long timeout,
-                long revokeAfterkilledDelay) {
+        private PackageInactivityListener(int uid, @NonNull String packageName, int deviceId,
+                long timeout, long revokeAfterkilledDelay) {
             Log.i(LOG_TAG,
                     "Start tracking " + packageName + ". uid=" + uid + " timeout=" + timeout
                             + " killedDelay=" + revokeAfterkilledDelay);
 
             mUid = uid;
             mPackageName = packageName;
+            mDeviceId = deviceId;
             mTimeout = timeout;
             mRevokeAfterKilledDelay = revokeAfterkilledDelay == -1
                     ? DeviceConfig.getLong(
@@ -232,7 +235,8 @@
                                 PROPERTY_KILLED_DELAY_CONFIG_KEY, DEFAULT_KILLED_DELAY_MILLIS)
                                 : revokeAfterKilledDelayMillis);
                 Log.v(LOG_TAG,
-                        "Updated params for " + mPackageName + ". timeout=" + mTimeout
+                        "Updated params for " + mPackageName + ", device ID " + mDeviceId
+                                + ". timeout=" + mTimeout
                                 + " killedDelay=" + mRevokeAfterKilledDelay);
                 updateUidState();
             }
@@ -260,7 +264,7 @@
 
         private void updateUidState(int state) {
             Log.v(LOG_TAG, "Updating state for " + mPackageName + " (" + mUid + ")."
-                    + " state=" + state);
+                    + " device ID=" + mDeviceId + ", state=" + state);
             synchronized (mInnerLock) {
                 // Remove any pending inactivity callback
                 mHandler.removeCallbacksAndMessages(mToken);
@@ -283,7 +287,7 @@
                         if (DEBUG) {
                             Log.d(LOG_TAG, "No longer gone after delayed revocation. "
                                     + "Rechecking for " + mPackageName + " (" + mUid
-                                    + ").");
+                                    + "). device ID " + mDeviceId);
                         }
                         updateUidState(currentState);
                     }, mToken, mRevokeAfterKilledDelay);
@@ -292,7 +296,7 @@
                     if (mTimerStart == TIMER_INACTIVE) {
                         if (DEBUG) {
                             Log.d(LOG_TAG, "Start the timer for "
-                                    + mPackageName + " (" + mUid + ").");
+                                    + mPackageName + " (" + mUid + "). device ID " + mDeviceId);
                         }
                         mTimerStart = System.currentTimeMillis();
                         setAlarmLocked();
@@ -329,7 +333,8 @@
             }
 
             if (DEBUG) {
-                Log.d(LOG_TAG, "Scheduling alarm for " + mPackageName + " (" + mUid + ").");
+                Log.d(LOG_TAG, "Scheduling alarm for " + mPackageName + " (" + mUid + ")."
+                        + " device ID " + mDeviceId);
             }
             long revokeTime = mTimerStart + mTimeout;
             if (revokeTime > System.currentTimeMillis()) {
@@ -349,7 +354,8 @@
         private void cancelAlarmLocked() {
             if (mIsAlarmSet) {
                 if (DEBUG) {
-                    Log.d(LOG_TAG, "Canceling alarm for " + mPackageName + " (" + mUid + ").");
+                    Log.d(LOG_TAG, "Canceling alarm for " + mPackageName + " (" + mUid + ")."
+                            + " device ID " + mDeviceId);
                 }
                 mAlarmManager.cancel(this);
                 mIsAlarmSet = false;
@@ -366,17 +372,17 @@
             }
             if (DEBUG) {
                 Log.d(LOG_TAG, "onPackageInactiveLocked stack trace for "
-                        + mPackageName + " (" + mUid + ").", new RuntimeException());
+                        + mPackageName + " (" + mUid + "). device ID " + mDeviceId,
+                        new RuntimeException());
             }
             mIsFinished = true;
             cancelAlarmLocked();
             mHandler.post(
                     () -> {
                         Log.i(LOG_TAG, "One time session expired for "
-                                + mPackageName + " (" + mUid + ").");
-
+                                + mPackageName + " (" + mUid + "). deviceID " + mDeviceId);
                         mPermissionControllerManager.notifyOneTimePermissionSessionTimeout(
-                                mPackageName);
+                                mPackageName, mDeviceId);
                     });
             try {
                 mIActivityManager.unregisterUidObserver(mObserver);
@@ -391,7 +397,8 @@
         @Override
         public void onAlarm() {
             if (DEBUG) {
-                Log.d(LOG_TAG, "Alarm received for " + mPackageName + " (" + mUid + ").");
+                Log.d(LOG_TAG, "Alarm received for " + mPackageName + " (" + mUid + ")."
+                        + " device ID " + mDeviceId);
             }
             synchronized (mInnerLock) {
                 if (!mIsAlarmSet) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 44eb28f..9610d05 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -404,15 +404,15 @@
 
     @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS)
     @Override
-    public void startOneTimePermissionSession(String packageName, @UserIdInt int userId,
-            long timeoutMillis, long revokeAfterKilledDelayMillis) {
+    public void startOneTimePermissionSession(String packageName, int deviceId,
+            @UserIdInt int userId, long timeoutMillis, long revokeAfterKilledDelayMillis) {
         startOneTimePermissionSession_enforcePermission();
         Objects.requireNonNull(packageName);
 
         final long token = Binder.clearCallingIdentity();
         try {
             getOneTimePermissionUserManager(userId).startPackageOneTimeSession(packageName,
-                    timeoutMillis, revokeAfterKilledDelayMillis);
+                    deviceId, timeoutMillis, revokeAfterKilledDelayMillis);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
index b2733d4..240585c 100644
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
@@ -16,6 +16,7 @@
 
 package com.android.server.permission.access.permission
 
+import android.permission.PermissionManager
 import android.util.Slog
 import com.android.modules.utils.BinaryXmlPullParser
 import com.android.modules.utils.BinaryXmlSerializer
@@ -164,9 +165,18 @@
         deviceId: String,
         userId: Int,
         permissionName: String
-    ): Int =
-        state.userStates[userId]?.appIdDevicePermissionFlags?.get(appId)?.get(deviceId)
-            ?.getWithDefault(permissionName, 0) ?: 0
+    ): Int {
+        val flags = state.userStates[userId]?.appIdDevicePermissionFlags?.get(appId)?.get(deviceId)
+                ?.getWithDefault(permissionName, 0) ?: 0
+        if (PermissionManager.DEBUG_DEVICE_PERMISSIONS) {
+            Slog.i(
+                LOG_TAG, "getPermissionFlags: appId=$appId, userId=$userId," +
+                    " deviceId=$deviceId, permissionName=$permissionName," +
+                    " flags=${PermissionFlags.toString(flags)}"
+            )
+        }
+        return flags
+    }
 
     fun MutateStateScope.setPermissionFlags(
         appId: Int,
@@ -202,6 +212,13 @@
         val devicePermissionFlags = appIdDevicePermissionFlags.mutateOrPut(appId) {
             MutableIndexedReferenceMap()
         }
+        if (PermissionManager.DEBUG_DEVICE_PERMISSIONS) {
+            Slog.i(
+                LOG_TAG, "setPermissionFlags(): appId=$appId, userId=$userId," +
+                    " deviceId=$deviceId, permissionName=$permissionName," +
+                    " newFlags=${PermissionFlags.toString(newFlags)}"
+            )
+        }
         val permissionFlags = devicePermissionFlags.mutateOrPut(deviceId) { MutableIndexedMap() }
         permissionFlags.putWithDefault(permissionName, newFlags, 0)
         if (permissionFlags.isEmpty()) {