Test backup + restore of permissions

- foreground/background permissions
- permission groups
- pre-M apps
- user-set/user-fixed/review-required flag
- delayed permission restore

Test: atest CtsBackupTestCases:android.backup.cts.PermissionTest
Change-Id: Ic925625ecc1679e9041650ad060e40cf11b38744
diff --git a/tests/backup/AndroidTest.xml b/tests/backup/AndroidTest.xml
index 635bfdc..a7081d6 100644
--- a/tests/backup/AndroidTest.xml
+++ b/tests/backup/AndroidTest.xml
@@ -26,6 +26,17 @@
         <option name="test-file-name" value="CtsKeyValueBackupApp.apk" />
         <option name="test-file-name" value="CtsBackupTestCases.apk" />
         <option name="test-file-name" value="CtsPermissionBackupApp.apk" />
+        <option name="test-file-name" value="CtsPermissionBackupApp22.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="mkdir -p /data/local/tmp/cts/backup" />
+        <option name="teardown-command" value="rm -rf /data/local/tmp/cts"/>
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <!-- Also copy the test apks to the device -->
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsPermissionBackupApp.apk->/data/local/tmp/cts/backup/CtsPermissionBackupApp.apk" />
+        <option name="push" value="CtsPermissionBackupApp22.apk->/data/local/tmp/cts/backup/CtsPermissionBackupApp22.apk" />
     </target_preparer>
     <target_preparer class="android.cts.backup.BackupPreparer">
         <option name="enable-backup-if-needed" value="true" />
diff --git a/tests/backup/app/permission/AndroidManifest.xml b/tests/backup/app/permission/AndroidManifest.xml
index 8dd735a..7dd4701 100644
--- a/tests/backup/app/permission/AndroidManifest.xml
+++ b/tests/backup/app/permission/AndroidManifest.xml
@@ -18,8 +18,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="android.backup.permission" >
 
-    <uses-permission android:name="android.permission.READ_SMS"/>
-    <uses-permission android:name="android.permission.SEND_SMS"/>
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
     <uses-permission android:name="android.permission.READ_CONTACTS"/>
     <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
 
diff --git a/tests/backup/app/permission22/Android.mk b/tests/backup/app/permission22/Android.mk
new file mode 100644
index 0000000..8790e19
--- /dev/null
+++ b/tests/backup/app/permission22/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
+
+LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
+LOCAL_SDK_VERSION := current
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/backup/app/permission22/AndroidManifest.xml b/tests/backup/app/permission22/AndroidManifest.xml
new file mode 100644
index 0000000..d82d5bf
--- /dev/null
+++ b/tests/backup/app/permission22/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.backup.permission22" >
+
+    <uses-sdk android:targetSdkVersion="22" />
+
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.READ_CONTACTS"/>
+    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+
+    <application android:label="Android Permission Backup CTS App (API 22)" />
+</manifest>
diff --git a/tests/backup/src/android/backup/cts/PermissionTest.java b/tests/backup/src/android/backup/cts/PermissionTest.java
index bf81def..7b64efb 100644
--- a/tests/backup/src/android/backup/cts/PermissionTest.java
+++ b/tests/backup/src/android/backup/cts/PermissionTest.java
@@ -16,31 +16,61 @@
 
 package android.backup.cts;
 
-import static com.android.compatibility.common.util.BackupUtils.LOCAL_TRANSPORT_TOKEN;
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.READ_CONTACTS;
+import static android.Manifest.permission.WRITE_CONTACTS;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_FOREGROUND;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.permissionToOp;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
-import android.Manifest;
-import android.app.Instrumentation;
-import android.content.pm.PackageManager;
+import static com.android.compatibility.common.util.BackupUtils.LOCAL_TRANSPORT_TOKEN;
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import android.app.AppOpsManager;
+import android.content.Context;
 import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.AppModeFull;
+import android.support.test.InstrumentationRegistry;
+
+import androidx.annotation.NonNull;
 
 import com.android.compatibility.common.util.BackupUtils;
+import com.android.compatibility.common.util.ShellUtils;
 
 import java.io.IOException;
 import java.io.InputStream;
 
 /**
  * Verifies that restored permissions are the same with backup value.
+ *
+ * @see com.android.packageinstaller.permission.service.BackupHelper
  */
 @AppModeFull
 public class PermissionTest extends BaseBackupCtsTest {
 
-    /** The name of the package of the app under test */
-    private static final String APP_PACKAGE = "android.backup.permission";
+    /** The name of the package of the apps under test */
+    private static final String APP = "android.backup.permission";
+    private static final String APP22 = "android.backup.permission22";
+
+    /** The apk of the packages */
+    private static final String APK_PATH = "/data/local/tmp/cts/backup/";
+    private static final String APP_APK = APK_PATH + "CtsPermissionBackupApp.apk";
+    private static final String APP22_APK = APK_PATH + "CtsPermissionBackupApp22.apk";
 
     /** The name of the package for backup */
     private static final String ANDROID_PACKAGE = "android";
 
+    private static final Context sContext = InstrumentationRegistry.getTargetContext();
+    private static final long TIMEOUT_MILLIS = 10000;
+
     private BackupUtils mBackupUtils =
             new BackupUtils() {
                 @Override
@@ -51,49 +81,328 @@
                 }
             };
 
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        resetApp(APP);
+        resetApp(APP22);
+    }
+
     /**
-     * Test backup and restore of grant runtime permission.
-     *
-     * Test logic:
-     * 1. Grant SEND_SMS and WRITE_CONTACTS permissions to APP_PACKAGE.
-     * 2. Backup android package, revoke SEND_SMS and WRITE_CONTACTS permissions to APP_PACKAGE.
-     * Then restore android package.
-     * 3. Check restored SEND_SMS and WRITE_CONTACTS permissions.
-     *
-     * @see PackageManagerService#serializeRuntimePermissionGrantsLPr(XmlSerializer, int) and
-     * PackageManagerService#processRestoredPermissionGrantsLPr(XmlPullParser, int)
+     * Test backup and restore of regular runtime permission.
      */
-    public void testGrantRuntimePermission() throws Exception {
-        grantRuntimePermission(Manifest.permission.SEND_SMS);
-        grantRuntimePermission(Manifest.permission.WRITE_CONTACTS);
+    public void testGrantDeniedRuntimePermission() throws Exception {
+        grantRuntimePermission(APP, ACCESS_FINE_LOCATION);
 
         mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
-        revokeRuntimePermission(Manifest.permission.SEND_SMS);
-        revokeRuntimePermission(Manifest.permission.WRITE_CONTACTS);
+        resetApp(APP);
         mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
 
-        assertEquals(PackageManager.PERMISSION_GRANTED,
-                checkPermission(Manifest.permission.SEND_SMS));
-        assertEquals(PackageManager.PERMISSION_GRANTED,
-                checkPermission(Manifest.permission.WRITE_CONTACTS));
+        eventually(() -> {
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION));
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS));
+        });
     }
 
-    private int checkPermission(String permission) {
-        return getInstrumentation().getContext().getPackageManager().checkPermission(permission,
-                APP_PACKAGE);
+    /**
+     * Test backup and restore of pre-M regular runtime permission.
+     */
+    public void testGrantDeniedRuntimePermission22() throws Exception {
+        setAppOp(APP22, READ_CONTACTS, MODE_IGNORED);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        resetApp(APP22);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        eventually(() -> {
+            assertEquals(MODE_IGNORED, getAppOp(APP22, READ_CONTACTS));
+            assertEquals(MODE_ALLOWED, getAppOp(APP22, ACCESS_FINE_LOCATION));
+        });
     }
 
-    private void grantRuntimePermission(String permission) {
-        if (checkPermission(permission) != PackageManager.PERMISSION_GRANTED) {
-            getInstrumentation().getUiAutomation().grantRuntimePermission(APP_PACKAGE, permission);
-            assertEquals(PackageManager.PERMISSION_GRANTED, checkPermission(permission));
+    /**
+     * Test backup and restore of foreground runtime permission.
+     */
+    public void testNoTriStateRuntimePermission() throws Exception {
+        // Set a marker
+        grantRuntimePermission(APP, WRITE_CONTACTS);
+
+        // revoked is the default state. Hence mark the permissions as user set, so the permissions
+        // are even backed up
+        setFlag(APP, ACCESS_FINE_LOCATION, FLAG_PERMISSION_USER_SET);
+        setFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        resetApp(APP);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        eventually(() -> {
+            // Wait until marker is set
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, WRITE_CONTACTS));
+
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION));
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION));
+            assertEquals(MODE_IGNORED, getAppOp(APP, ACCESS_FINE_LOCATION));
+        });
+    }
+
+    /**
+     * Test backup and restore of foreground runtime permission.
+     */
+    public void testNoTriStateRuntimePermission22() throws Exception {
+        setAppOp(APP22, ACCESS_FINE_LOCATION, MODE_IGNORED);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        resetApp(APP22);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        eventually(() -> assertEquals(MODE_IGNORED, getAppOp(APP22, ACCESS_FINE_LOCATION)));
+    }
+
+    /**
+     * Test backup and restore of foreground runtime permission.
+     */
+    public void testGrantForegroundRuntimePermission() throws Exception {
+        grantRuntimePermission(APP, ACCESS_FINE_LOCATION);
+        setAppOp(APP, ACCESS_FINE_LOCATION, MODE_FOREGROUND);
+
+        // revoked is the default state. Hence mark the permission as user set, so the permissions
+        // are even backed up
+        setFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        resetApp(APP);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        eventually(() -> {
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION));
+            assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION));
+            assertEquals(MODE_FOREGROUND, getAppOp(APP, ACCESS_FINE_LOCATION));
+        });
+    }
+
+    /**
+     * Test backup and restore of foreground runtime permission.
+     */
+    public void testGrantForegroundRuntimePermission22() throws Exception {
+        setAppOp(APP22, ACCESS_FINE_LOCATION, MODE_FOREGROUND);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        resetApp(APP22);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        eventually(() -> assertEquals(MODE_FOREGROUND, getAppOp(APP22, ACCESS_FINE_LOCATION)));
+    }
+
+    /**
+     * Test backup and restore of foreground runtime permission.
+     */
+    public void testGrantForegroundAndBackgroundRuntimePermission() throws Exception {
+        grantRuntimePermission(APP, ACCESS_FINE_LOCATION);
+        grantRuntimePermission(APP, ACCESS_BACKGROUND_LOCATION);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        resetApp(APP);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        eventually(() -> {
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION));
+            assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION));
+            assertEquals(MODE_ALLOWED, getAppOp(APP, ACCESS_FINE_LOCATION));
+        });
+    }
+
+    /**
+     * Test backup and restore of foreground runtime permission.
+     */
+    public void testGrantForegroundAndBackgroundRuntimePermission22() throws Exception {
+        // Set a marker
+        setAppOp(APP, WRITE_CONTACTS, MODE_IGNORED);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        resetApp(APP22);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        eventually(() -> {
+            // Wait for marker
+            assertEquals(MODE_IGNORED, getAppOp(APP, WRITE_CONTACTS));
+
+            assertEquals(MODE_ALLOWED, getAppOp(APP, ACCESS_FINE_LOCATION));
+        });
+    }
+
+    /**
+     * Restore if the permission was reviewed
+     */
+    public void testRestorePermReviewed() throws Exception {
+        clearFlag(APP22, WRITE_CONTACTS, FLAG_PERMISSION_REVIEW_REQUIRED);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        resetApp(APP22);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        eventually(() -> assertFalse(
+                isFlagSet(APP22, WRITE_CONTACTS, FLAG_PERMISSION_REVIEW_REQUIRED)));
+    }
+
+    /**
+     * Restore if the permission was user set
+     */
+    public void testRestoreUserSet() throws Exception {
+        setFlag(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_SET);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        resetApp(APP);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        eventually(() -> assertTrue(isFlagSet(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_SET)));
+    }
+
+    /**
+     * Restore if the permission was user fixed
+     */
+    public void testRestoreUserFixed() throws Exception {
+        setFlag(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_FIXED);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        resetApp(APP);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        eventually(() -> assertTrue(isFlagSet(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_FIXED)));
+    }
+
+    /**
+     * Restoring of a flag should not grant the permission
+     */
+    public void testRestoreOfFlagDoesNotGrantPermission() throws Exception {
+        setFlag(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_FIXED);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        resetApp(APP);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        eventually(() -> assertEquals(PERMISSION_DENIED, checkPermission(APP, WRITE_CONTACTS)));
+    }
+
+    /**
+     * Test backup and delayed restore of regular runtime permission.
+     */
+    public void testDelayedRestore() throws IOException {
+        grantRuntimePermission(APP, ACCESS_FINE_LOCATION);
+
+        setAppOp(APP22, READ_CONTACTS, MODE_IGNORED);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+
+        uninstall(APP);
+        uninstall(APP22);
+
+        try {
+            mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+            install(APP_APK);
+
+            eventually(() -> assertEquals(PERMISSION_GRANTED,
+                    checkPermission(APP, ACCESS_FINE_LOCATION)));
+
+            install(APP22_APK);
+
+            eventually(() -> assertEquals(MODE_IGNORED, getAppOp(APP22, READ_CONTACTS)));
+        } finally {
+            install(APP_APK);
+            install(APP22_APK);
         }
     }
 
-    private void revokeRuntimePermission(String permission) {
-        if (checkPermission(permission) == PackageManager.PERMISSION_GRANTED) {
-            getInstrumentation().getUiAutomation().revokeRuntimePermission(APP_PACKAGE, permission);
-            assertEquals(PackageManager.PERMISSION_DENIED, checkPermission(permission));
+    private void install(String apk) {
+        ShellUtils.runShellCommand("pm install -r " + apk);
+    }
+
+    private void uninstall(String packageName) {
+        ShellUtils.runShellCommand("pm uninstall " + packageName);
+    }
+
+    private void resetApp(String packageName) {
+        ShellUtils.runShellCommand("pm clear " + packageName);
+        ShellUtils.runShellCommand("appops reset " + packageName);
+    }
+
+    /**
+     * Make sure that a {@link Runnable} eventually finishes without throwing a {@link
+     * Exception}.
+     *
+     * @param r The {@link Runnable} to run.
+     */
+    public static void eventually(@NonNull Runnable r) {
+        long start = System.currentTimeMillis();
+
+        while (true) {
+            try {
+                r.run();
+                return;
+            } catch (Throwable e) {
+                if (System.currentTimeMillis() - start < TIMEOUT_MILLIS) {
+                    try {
+                        Thread.sleep(100);
+                    } catch (InterruptedException ignored) {
+                        throw new RuntimeException(e);
+                    }
+                } else {
+                    throw e;
+                }
+            }
+        }
+    }
+
+    private void setFlag(String app, String permission, int flag) {
+        runWithShellPermissionIdentity(
+                () -> sContext.getPackageManager().updatePermissionFlags(permission, app,
+                        flag, flag, sContext.getUser()));
+    }
+
+    private void clearFlag(String app, String permission, int flag) {
+        runWithShellPermissionIdentity(
+                () -> sContext.getPackageManager().updatePermissionFlags(permission, app,
+                        flag, 0, sContext.getUser()));
+    }
+
+    private boolean isFlagSet(String app, String permission, int flag) {
+        try {
+            return (callWithShellPermissionIdentity(
+                    () -> sContext.getPackageManager().getPermissionFlags(permission, app,
+                            sContext.getUser())) & flag) == flag;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private int checkPermission(String app, String permission) {
+        return sContext.getPackageManager().checkPermission(permission, app);
+    }
+
+    private void grantRuntimePermission(String app, String permission) {
+        if (checkPermission(app, permission) != PERMISSION_GRANTED) {
+            getInstrumentation().getUiAutomation().grantRuntimePermission(app, permission);
+            assertEquals(PERMISSION_GRANTED, checkPermission(app, permission));
+        }
+    }
+
+    private void setAppOp(String app, String permission, int mode) {
+        runWithShellPermissionIdentity(
+                () -> sContext.getSystemService(AppOpsManager.class).setUidMode(
+                        permissionToOp(permission),
+                        sContext.getPackageManager().getPackageUid(app, 0), mode));
+    }
+
+    private int getAppOp(String app, String permission) {
+        try {
+            return callWithShellPermissionIdentity(
+                    () -> sContext.getSystemService(AppOpsManager.class).unsafeCheckOpRaw(
+                            permissionToOp(permission),
+                            sContext.getPackageManager().getPackageUid(app, 0), app));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
         }
     }
 }