diff options
6 files changed, 650 insertions, 3 deletions
diff --git a/services/permission/Android.bp b/services/permission/Android.bp index dc9b5585cbf2..59ca8512ab8a 100644 --- a/services/permission/Android.bp +++ b/services/permission/Android.bp @@ -17,8 +17,8 @@ filegroup { visibility: ["//frameworks/base/services"], } -java_library_static { - name: "services.permission", +java_library { + name: "services.permission-pre-jarjar", defaults: ["platform_service_defaults"], srcs: [":services.permission-sources"], libs: [ @@ -32,7 +32,6 @@ java_library_static { // Adds reflection-less suppressed exceptions and AutoCloseable.use(). "kotlin-stdlib-jdk7", ], - jarjar_rules: "jarjar-rules.txt", kotlincflags: [ "-Xjvm-default=all", "-Xno-call-assertions", @@ -40,3 +39,9 @@ java_library_static { "-Xno-receiver-assertions", ], } + +java_library { + name: "services.permission", + static_libs: ["services.permission-pre-jarjar"], + jarjar_rules: "jarjar-rules.txt", +} diff --git a/services/tests/PermissionServiceMockingTests/Android.bp b/services/tests/PermissionServiceMockingTests/Android.bp new file mode 100644 index 000000000000..cedfd2c43e93 --- /dev/null +++ b/services/tests/PermissionServiceMockingTests/Android.bp @@ -0,0 +1,44 @@ +// Copyright (C) 2023 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "PermissionServiceMockingTests", + defaults: [ + // This is needed for the ExtendedMockitoRule + "modules-utils-testable-device-config-defaults", + ], + srcs: [ + "src/**/*.kt", + ], + static_libs: [ + "mockingservicestests-utils-mockito", + "services.core", + "services.permission-pre-jarjar", + "servicestests-core-utils", + "servicestests-utils", + ], + platform_apis: true, + test_suites: [ + "device-tests", + ], +} diff --git a/services/tests/PermissionServiceMockingTests/AndroidManifest.xml b/services/tests/PermissionServiceMockingTests/AndroidManifest.xml new file mode 100644 index 000000000000..32b64ac1a524 --- /dev/null +++ b/services/tests/PermissionServiceMockingTests/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + Copyright (C) 2023 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.server.permission.test"> + + <application android:debuggable="true"> + <uses-library android:name="android.test.mock" android:required="true" /> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.permission.test" + android:label="Permission Service Mocking Tests" /> +</manifest> diff --git a/services/tests/PermissionServiceMockingTests/AndroidTest.xml b/services/tests/PermissionServiceMockingTests/AndroidTest.xml new file mode 100644 index 000000000000..157c4f06fb2a --- /dev/null +++ b/services/tests/PermissionServiceMockingTests/AndroidTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- Copyright (C) 2023 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. +--> +<configuration description="Configuration for PermissionServiceMockingTests"> + <option name="test-tag" value="PermissionServiceMockingTests" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="PermissionServiceMockingTests.apk" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.server.permission.test" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + </test> +</configuration> diff --git a/services/tests/PermissionServiceMockingTests/OWNERS b/services/tests/PermissionServiceMockingTests/OWNERS new file mode 100644 index 000000000000..dafdf0f8075c --- /dev/null +++ b/services/tests/PermissionServiceMockingTests/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/core/java/android/permission/OWNERS diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt new file mode 100644 index 000000000000..3ef3a89da94d --- /dev/null +++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2023 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.server.permission.test + +import android.content.pm.PackageManager +import android.content.pm.PermissionGroupInfo +import android.content.pm.PermissionInfo +import android.os.Bundle +import android.util.ArrayMap +import android.util.ArraySet +import android.util.SparseArray +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.modules.utils.testing.ExtendedMockitoRule +import com.android.server.extendedtestutils.wheneverStatic +import com.android.server.permission.access.MutableAccessState +import com.android.server.permission.access.MutableUserState +import com.android.server.permission.access.MutateStateScope +import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports +import com.android.server.permission.access.permission.AppIdPermissionPolicy +import com.android.server.permission.access.permission.Permission +import com.android.server.permission.access.permission.PermissionFlags +import com.android.server.pm.parsing.PackageInfoUtils +import com.android.server.pm.pkg.AndroidPackage +import com.android.server.pm.pkg.PackageState +import com.android.server.pm.pkg.PackageUserState +import com.android.server.pm.pkg.component.ParsedPermission +import com.android.server.pm.pkg.component.ParsedPermissionGroup +import com.android.server.testutils.mock +import com.android.server.testutils.whenever +import com.google.common.truth.Truth.assertWithMessage +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Mocking unit test for AppIdPermissionPolicy. + */ +@RunWith(AndroidJUnit4::class) +class AppIdPermissionPolicyTest { + private lateinit var oldState: MutableAccessState + private lateinit var newState: MutableAccessState + + private lateinit var androidPackage0: AndroidPackage + private lateinit var androidPackage1: AndroidPackage + + private lateinit var packageState0: PackageState + private lateinit var packageState1: PackageState + + private val appIdPermissionPolicy = AppIdPermissionPolicy() + + @Rule + @JvmField + val extendedMockitoRule = ExtendedMockitoRule.Builder(this) + .spyStatic(PackageInfoUtils::class.java) + .build() + + @Before + fun init() { + oldState = MutableAccessState() + createUserState(USER_ID_0) + createUserState(USER_ID_1) + oldState.mutateExternalState().setPackageStates(ArrayMap()) + + androidPackage0 = mockAndroidPackage( + PACKAGE_NAME_0, + PERMISSION_GROUP_NAME_0, + PERMISSION_NAME_0 + ) + androidPackage1 = mockAndroidPackage( + PACKAGE_NAME_1, + PERMISSION_GROUP_NAME_1, + PERMISSION_NAME_1 + ) + + packageState0 = mockPackageState(APP_ID_0, androidPackage0) + packageState1 = mockPackageState(APP_ID_1, androidPackage1) + } + + private fun mockAndroidPackage( + packageName: String, + permissionGroupName: String, + permissionName: String, + ): AndroidPackage { + val parsedPermissionGroup = mock<ParsedPermissionGroup> { + whenever(name).thenReturn(permissionGroupName) + whenever(metaData).thenReturn(Bundle()) + } + + @Suppress("DEPRECATION") + val permissionGroupInfo = PermissionGroupInfo().apply { + name = permissionGroupName + this.packageName = packageName + } + wheneverStatic { + PackageInfoUtils.generatePermissionGroupInfo( + parsedPermissionGroup, + PackageManager.GET_META_DATA.toLong() + ) + }.thenReturn(permissionGroupInfo) + + val parsedPermission = mock<ParsedPermission> { + whenever(name).thenReturn(permissionName) + whenever(isTree).thenReturn(false) + whenever(metaData).thenReturn(Bundle()) + } + + @Suppress("DEPRECATION") + val permissionInfo = PermissionInfo().apply { + name = permissionName + this.packageName = packageName + } + wheneverStatic { + PackageInfoUtils.generatePermissionInfo( + parsedPermission, + PackageManager.GET_META_DATA.toLong() + ) + }.thenReturn(permissionInfo) + + val requestedPermissions = ArraySet<String>() + return mock { + whenever(this.packageName).thenReturn(packageName) + whenever(this.requestedPermissions).thenReturn(requestedPermissions) + whenever(permissionGroups).thenReturn(listOf(parsedPermissionGroup)) + whenever(permissions).thenReturn(listOf(parsedPermission)) + whenever(signingDetails).thenReturn(mock {}) + } + } + + private fun mockPackageState( + appId: Int, + androidPackage: AndroidPackage, + ): PackageState { + val packageName = androidPackage.packageName + oldState.mutateExternalState().mutateAppIdPackageNames().mutateOrPut(appId) { + MutableIndexedListSet() + }.add(packageName) + + val userStates = SparseArray<PackageUserState>().apply { + put(USER_ID_0, mock { whenever(isInstantApp).thenReturn(false) }) + } + val mockPackageState: PackageState = mock { + whenever(this.packageName).thenReturn(packageName) + whenever(this.appId).thenReturn(appId) + whenever(this.androidPackage).thenReturn(androidPackage) + whenever(isSystem).thenReturn(false) + whenever(this.userStates).thenReturn(userStates) + } + oldState.mutateExternalState().setPackageStates( + oldState.mutateExternalState().packageStates.toMutableMap().apply { + put(packageName, mockPackageState) + } + ) + return mockPackageState + } + + private fun createUserState(userId: Int) { + oldState.mutateUserStatesNoWrite().put(userId, MutableUserState()) + } + + @Test + fun testResetRuntimePermissions_runtimeGranted_getsRevoked() { + val oldFlags = PermissionFlags.RUNTIME_GRANTED + val expectedNewFlags = 0 + testResetRuntimePermissions(oldFlags, expectedNewFlags) {} + } + + @Test + fun testResetRuntimePermissions_roleGranted_getsGranted() { + val oldFlags = PermissionFlags.ROLE + val expectedNewFlags = PermissionFlags.ROLE or PermissionFlags.RUNTIME_GRANTED + testResetRuntimePermissions(oldFlags, expectedNewFlags) {} + } + + @Test + fun testResetRuntimePermissions_nullAndroidPackage_remainsUnchanged() { + val oldFlags = PermissionFlags.RUNTIME_GRANTED + val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED + testResetRuntimePermissions(oldFlags, expectedNewFlags) { + whenever(packageState0.androidPackage).thenReturn(null) + } + } + + private inline fun testResetRuntimePermissions( + oldFlags: Int, + expectedNewFlags: Int, + additionalSetup: () -> Unit + ) { + createSystemStatePermission( + APP_ID_0, + PACKAGE_NAME_0, + PERMISSION_NAME_0, + PermissionInfo.PROTECTION_DANGEROUS + ) + androidPackage0.requestedPermissions.add(PERMISSION_NAME_0) + oldState.mutateUserState(USER_ID_0)!!.mutateAppIdPermissionFlags().mutateOrPut(APP_ID_0) { + MutableIndexedMap() + }.put(PERMISSION_NAME_0, oldFlags) + + additionalSetup() + + mutateState { + with(appIdPermissionPolicy) { + resetRuntimePermissions(PACKAGE_NAME_0, USER_ID_0) + } + } + + val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0) + assertWithMessage( + "After resetting runtime permissions, permission flags did not match" + + " expected values: expectedNewFlags is $expectedNewFlags," + + " actualFlags is $actualFlags, while the oldFlags is $oldFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testOnPackageAdded_permissionsOfMissingSystemApp_getsAdopted() { + testOnPackageAdded { + adoptPermissionTestSetup() + whenever(packageState1.androidPackage).thenReturn(null) + } + + val permission0 = newState.systemState.permissions[PERMISSION_NAME_0] + assertWithMessage( + "After onPackageAdded() is called for a null adopt permission package," + + " the permission package name: ${permission0!!.packageName} did not match" + + " the expected package name: $PACKAGE_NAME_0" + ) + .that(permission0.packageName) + .isEqualTo(PACKAGE_NAME_0) + } + + @Test + fun testOnPackageAdded_permissionsOfExistingSystemApp_notAdopted() { + testOnPackageAdded { + adoptPermissionTestSetup() + } + + val permission0 = newState.systemState.permissions[PERMISSION_NAME_0] + assertWithMessage( + "After onPackageAdded() is called for a non-null adopt permission" + + " package, the permission package name: ${permission0!!.packageName} should" + + " not match the package name: $PACKAGE_NAME_0" + ) + .that(permission0.packageName) + .isNotEqualTo(PACKAGE_NAME_0) + } + + @Test + fun testOnPackageAdded_permissionsOfNonSystemApp_notAdopted() { + testOnPackageAdded { + adoptPermissionTestSetup() + whenever(packageState1.isSystem).thenReturn(false) + } + + val permission0 = newState.systemState.permissions[PERMISSION_NAME_0] + assertWithMessage( + "After onPackageAdded() is called for a non-system adopt permission" + + " package, the permission package name: ${permission0!!.packageName} should" + + " not match the package name: $PACKAGE_NAME_0" + ) + .that(permission0.packageName) + .isNotEqualTo(PACKAGE_NAME_0) + } + + private fun adoptPermissionTestSetup() { + createSystemStatePermission( + APP_ID_1, + PACKAGE_NAME_1, + PERMISSION_NAME_0, + PermissionInfo.PROTECTION_SIGNATURE + ) + whenever(androidPackage0.adoptPermissions).thenReturn(listOf(PACKAGE_NAME_1)) + whenever(packageState1.isSystem).thenReturn(true) + } + + @Test + fun testOnPackageAdded_newPermissionGroup_getsDeclared() { + testOnPackageAdded {} + + assertWithMessage( + "After onPackageAdded() is called when there is no existing" + + " permission groups, the new permission group $PERMISSION_GROUP_NAME_0 is not added" + ) + .that(newState.systemState.permissionGroups[PERMISSION_GROUP_NAME_0]?.name) + .isEqualTo(PERMISSION_GROUP_NAME_0) + } + + @Test + fun testOnPackageAdded_systemAppTakingOverPermissionGroupDefinition_getsTakenOver() { + testOnPackageAdded { + whenever(packageState0.isSystem).thenReturn(true) + createSystemStatePermissionGroup(PACKAGE_NAME_1, PERMISSION_GROUP_NAME_0) + } + + assertWithMessage( + "After onPackageAdded() is called when $PERMISSION_GROUP_NAME_0 already" + + " exists in the system, the system app $PACKAGE_NAME_0 didn't takeover the ownership" + + " of this permission group" + ) + .that(newState.systemState.permissionGroups[PERMISSION_GROUP_NAME_0]?.packageName) + .isEqualTo(PACKAGE_NAME_0) + } + + @Test + fun testOnPackageAdded_instantApps_remainsUnchanged() { + testOnPackageAdded { + (packageState0.userStates as SparseArray<PackageUserState>).apply { + put(0, mock { whenever(isInstantApp).thenReturn(true) }) + } + } + + assertWithMessage( + "After onPackageAdded() is called for an instant app," + + " the new permission group $PERMISSION_GROUP_NAME_0 should not be added" + ) + .that(newState.systemState.permissionGroups[PERMISSION_GROUP_NAME_0]) + .isNull() + } + + @Test + fun testOnPackageAdded_nonSystemAppTakingOverPermissionGroupDefinition_remainsUnchanged() { + testOnPackageAdded { + createSystemStatePermissionGroup(PACKAGE_NAME_1, PERMISSION_GROUP_NAME_0) + } + + assertWithMessage( + "After onPackageAdded() is called when $PERMISSION_GROUP_NAME_0 already" + + " exists in the system, non-system app $PACKAGE_NAME_0 shouldn't takeover ownership" + + " of this permission group" + ) + .that(newState.systemState.permissionGroups[PERMISSION_GROUP_NAME_0]?.packageName) + .isEqualTo(PACKAGE_NAME_1) + } + + @Test + fun testOnPackageAdded_takingOverPermissionGroupDeclaredBySystemApp_remainsUnchanged() { + testOnPackageAdded { + whenever(packageState1.isSystem).thenReturn(true) + createSystemStatePermissionGroup(PACKAGE_NAME_1, PERMISSION_GROUP_NAME_0) + } + + assertWithMessage( + "After onPackageAdded() is called when $PERMISSION_GROUP_NAME_0 already" + + " exists in the system and is owned by a system app, app $PACKAGE_NAME_0 shouldn't" + + " takeover ownership of this permission group" + ) + .that(newState.systemState.permissionGroups[PERMISSION_GROUP_NAME_0]?.packageName) + .isEqualTo(PACKAGE_NAME_1) + } + + @Test + fun testOnPackageAdded_newPermission_getsDeclared() { + testOnPackageAdded {} + + assertWithMessage( + "After onPackageAdded() is called when there is no existing" + + " permissions, the new permission $PERMISSION_NAME_0 is not added" + ) + .that(newState.systemState.permissions[PERMISSION_NAME_0]?.name) + .isEqualTo(PERMISSION_NAME_0) + } + + @Test + fun testOnPackageAdded_configPermission_getsTakenOver() { + testOnPackageAdded { + whenever(packageState0.isSystem).thenReturn(true) + createSystemStatePermission( + APP_ID_0, + PACKAGE_NAME_1, + PERMISSION_NAME_0, + PermissionInfo.PROTECTION_DANGEROUS, + Permission.TYPE_CONFIG, + false + ) + } + + assertWithMessage( + "After onPackageAdded() is called for a config permission with" + + " no owner, the ownership is not taken over by a system app $PACKAGE_NAME_0" + ) + .that(newState.systemState.permissions[PERMISSION_NAME_0]?.packageName) + .isEqualTo(PACKAGE_NAME_0) + } + + @Test + fun testOnPackageAdded_systemAppTakingOverPermissionDefinition_getsTakenOver() { + testOnPackageAdded { + whenever(packageState0.isSystem).thenReturn(true) + createSystemStatePermission( + APP_ID_1, + PACKAGE_NAME_1, + PERMISSION_NAME_0, + PermissionInfo.PROTECTION_DANGEROUS + ) + } + + assertWithMessage( + "After onPackageAdded() is called when $PERMISSION_NAME_0 already" + + " exists in the system, the system app $PACKAGE_NAME_0 didn't takeover the ownership" + + " of this permission" + ) + .that(newState.systemState.permissions[PERMISSION_NAME_0]?.packageName) + .isEqualTo(PACKAGE_NAME_0) + } + + @Test + fun testOnPackageAdded_nonSystemAppTakingOverPermissionDefinition_remainsUnchanged() { + testOnPackageAdded { + createSystemStatePermission( + APP_ID_1, + PACKAGE_NAME_1, + PERMISSION_NAME_0, + PermissionInfo.PROTECTION_DANGEROUS + ) + } + + assertWithMessage( + "After onPackageAdded() is called when $PERMISSION_NAME_0 already" + + " exists in the system, the non-system app $PACKAGE_NAME_0 shouldn't takeover" + + " ownership of this permission" + ) + .that(newState.systemState.permissions[PERMISSION_NAME_0]?.packageName) + .isEqualTo(PACKAGE_NAME_1) + } + + @Test + fun testOnPackageAdded_takingOverPermissionDeclaredBySystemApp_remainsUnchanged() { + testOnPackageAdded { + whenever(packageState1.isSystem).thenReturn(true) + createSystemStatePermission( + APP_ID_1, + PACKAGE_NAME_1, + PERMISSION_NAME_0, + PermissionInfo.PROTECTION_DANGEROUS + ) + } + + assertWithMessage( + "After onPackageAdded() is called when $PERMISSION_NAME_0 already" + + " exists in system and is owned by a system app, the app $PACKAGE_NAME_0 shouldn't" + + " takeover ownership of this permission" + ) + .that(newState.systemState.permissions[PERMISSION_NAME_0]?.packageName) + .isEqualTo(PACKAGE_NAME_1) + } + + private inline fun testOnPackageAdded(mockBehaviorOverride: () -> Unit) { + mockBehaviorOverride() + + mutateState { + with(appIdPermissionPolicy) { + onPackageAdded(packageState0) + } + } + } + + private inline fun mutateState(action: MutateStateScope.() -> Unit) { + newState = oldState.toMutable() + MutateStateScope(oldState, newState).action() + } + + private fun createSystemStatePermission( + appId: Int, + packageName: String, + permissionName: String, + protectionLevel: Int, + type: Int = Permission.TYPE_MANIFEST, + isReconciled: Boolean = true, + isTree: Boolean = false + ) { + @Suppress("DEPRECATION") + val permissionInfo = PermissionInfo().apply { + name = permissionName + this.packageName = packageName + this.protectionLevel = protectionLevel + } + val permission = Permission(permissionInfo, isReconciled, type, appId) + if (isTree) { + oldState.mutateSystemState().mutatePermissionTrees().put(permissionName, permission) + } else { + oldState.mutateSystemState().mutatePermissions().put(permissionName, permission) + } + } + + private fun createSystemStatePermissionGroup(packageName: String, permissionGroupName: String) { + @Suppress("DEPRECATION") + val permissionGroupInfo = PermissionGroupInfo().apply { + name = permissionGroupName + this.packageName = packageName + } + oldState.mutateSystemState().mutatePermissionGroups()[permissionGroupName] = + permissionGroupInfo + } + + fun getPermissionFlags( + appId: Int, + userId: Int, + permissionName: String, + state: MutableAccessState = newState + ): Int = + state.userStates[userId]?.appIdPermissionFlags?.get(appId).getWithDefault(permissionName, 0) + + companion object { + private const val PACKAGE_NAME_0 = "packageName0" + private const val PACKAGE_NAME_1 = "packageName1" + + private const val APP_ID_0 = 0 + private const val APP_ID_1 = 1 + + private const val PERMISSION_NAME_0 = "permissionName0" + private const val PERMISSION_NAME_1 = "permissionName1" + + private const val PERMISSION_GROUP_NAME_0 = "permissionGroupName0" + private const val PERMISSION_GROUP_NAME_1 = "permissionGroupName1" + + private const val USER_ID_0 = 0 + private const val USER_ID_1 = 1 + } +} |