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

Commit 5a34e929 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I2879a937,If1615aae

* changes:
  Use immutable collections.
  Add immutable collections.
parents f6b277df a8b3e27e
Loading
Loading
Loading
Loading
+11 −8
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import com.android.server.SystemService
import com.android.server.appop.AppOpsCheckingServiceInterface
import com.android.server.permission.access.appop.AppOpService
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.permission.PermissionService
import com.android.server.pm.KnownPackages
import com.android.server.pm.PackageManagerLocal
@@ -72,7 +73,7 @@ class AccessCheckingService(context: Context) : SystemService(context) {
        userManagerService = UserManagerService.getInstance()
        systemConfig = SystemConfig.getInstance()

        val userIds = IntSet(userManagerService.userIdsIncludingPreCreated)
        val userIds = MutableIntSet(userManagerService.userIdsIncludingPreCreated)
        val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
        val knownPackages = packageManagerInternal.knownPackages
        val isLeanback = systemConfig.isLeanback
@@ -82,7 +83,7 @@ class AccessCheckingService(context: Context) : SystemService(context) {
        val permissionAllowlist = systemConfig.permissionAllowlist
        val implicitToSourcePermissions = systemConfig.implicitToSourcePermissions

        val state = AccessState()
        val state = MutableAccessState()
        policy.initialize(
            state, userIds, packageStates, disabledSystemPackageStates, knownPackages, isLeanback,
            configPermissions, privilegedPermissionAllowlistPackages, permissionAllowlist,
@@ -104,7 +105,7 @@ class AccessCheckingService(context: Context) : SystemService(context) {
        get() = PackageManager.FEATURE_LEANBACK in availableFeatures

    private val SystemConfig.privilegedPermissionAllowlistPackages: IndexedListSet<String>
        get() = IndexedListSet<String>().apply {
        get() = MutableIndexedListSet<String>().apply {
            this += "android"
            if (PackageManager.FEATURE_AUTOMOTIVE in availableFeatures) {
                // Note that SystemProperties.get(String, String) forces returning an empty string
@@ -117,14 +118,16 @@ class AccessCheckingService(context: Context) : SystemService(context) {
        }

    private val SystemConfig.implicitToSourcePermissions: IndexedMap<String, IndexedListSet<String>>
        get() = IndexedMap<String, IndexedListSet<String>>().apply {
        @Suppress("UNCHECKED_CAST")
        get() = MutableIndexedMap<String, MutableIndexedListSet<String>>().apply {
            splitPermissions.forEach { splitPermissionInfo ->
                val sourcePermissionName = splitPermissionInfo.splitPermission
                splitPermissionInfo.newPermissions.forEach { implicitPermissionName ->
                    getOrPut(implicitPermissionName) { IndexedListSet() } += sourcePermissionName
                }
                    getOrPut(implicitPermissionName) { MutableIndexedListSet() } +=
                        sourcePermissionName
                }
            }
        } as IndexedMap<String, IndexedListSet<String>>

    fun getDecision(subject: AccessUri, `object`: AccessUri): Int =
        getState {
@@ -222,7 +225,7 @@ class AccessCheckingService(context: Context) : SystemService(context) {
        get() = withUnfilteredSnapshot().use { it.packageStates to it.disabledSystemPackageStates }

    private val PackageManagerInternal.knownPackages: IntMap<Array<String>>
        get() = IntMap<Array<String>>().apply {
        get() = MutableIntMap<Array<String>>().apply {
            this[KnownPackages.PACKAGE_INSTALLER] = getKnownPackageNames(
                KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM
            )
@@ -269,7 +272,7 @@ class AccessCheckingService(context: Context) : SystemService(context) {
        contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
        synchronized(stateLock) {
            val oldState = state
            val newState = oldState.copy()
            val newState = oldState.toMutable()
            MutateStateScope(oldState, newState).action()
            persistence.write(newState)
            state = newState
+15 −30
Original line number Diff line number Diff line
@@ -23,11 +23,13 @@ import android.os.SystemClock
import android.os.UserHandle
import android.util.AtomicFile
import android.util.Log
import android.util.SparseLongArray
import com.android.internal.annotations.GuardedBy
import com.android.internal.os.BackgroundThread
import com.android.modules.utils.BinaryXmlPullParser
import com.android.modules.utils.BinaryXmlSerializer
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.util.PermissionApex
import com.android.server.permission.access.util.parseBinaryXml
import com.android.server.permission.access.util.readWithReserveCopy
@@ -41,9 +43,9 @@ class AccessPersistence(
) {
    private val scheduleLock = Any()
    @GuardedBy("scheduleLock")
    private val pendingMutationTimesMillis = IntLongMap()
    private val pendingMutationTimesMillis = SparseLongArray()
    @GuardedBy("scheduleLock")
    private val pendingStates = IntMap<AccessState>()
    private val pendingStates = MutableIntMap<AccessState>()
    @GuardedBy("scheduleLock")
    private lateinit var writeHandler: WriteHandler

@@ -56,14 +58,14 @@ class AccessPersistence(
    /**
     * Reads the state either from the disk or migrate legacy data when the data files are missing.
     */
    fun read(state: AccessState) {
    fun read(state: MutableAccessState) {
        readSystemState(state)
        state.systemState.userIds.forEachIndexed { _, userId ->
            readUserState(state, userId)
        }
    }

    private fun readSystemState(state: AccessState) {
    private fun readSystemState(state: MutableAccessState) {
        val fileExists = systemFile.parse {
            // This is the canonical way to call an extension function in a different class.
            // TODO(b/259469752): Use context receiver for this when it becomes stable.
@@ -72,25 +74,18 @@ class AccessPersistence(

        if (!fileExists) {
            policy.migrateSystemState(state)
            state.systemState.apply {
                requestWrite()
                write(state, UserHandle.USER_ALL)
            }
            state.systemState.write(state, UserHandle.USER_ALL)
        }
    }


    private fun readUserState(state: AccessState, userId: Int) {
    private fun readUserState(state: MutableAccessState, userId: Int) {
        val fileExists = getUserFile(userId).parse {
            with(policy) { parseUserState(state, userId) }
        }

        if (!fileExists) {
            policy.migrateUserState(state, userId)
            state.userStates[userId].apply {
                requestWrite()
                write(state, userId)
            }
            state.userStates[userId]!!.write(state, userId)
        }
    }

@@ -119,11 +114,7 @@ class AccessPersistence(
    private fun WritableState.write(state: AccessState, userId: Int) {
        when (val writeMode = writeMode) {
            WriteMode.NONE -> {}
            WriteMode.SYNC -> {
                synchronized(scheduleLock) { pendingStates[userId] = state }
                writePendingState(userId)
            }
            WriteMode.ASYNC -> {
            WriteMode.ASYNCHRONOUS -> {
                synchronized(scheduleLock) {
                    writeHandler.removeMessages(userId)
                    pendingStates[userId] = state
@@ -142,6 +133,10 @@ class AccessPersistence(
                    }
                }
            }
            WriteMode.SYNCHRONOUS -> {
                synchronized(scheduleLock) { pendingStates[userId] = state }
                writePendingState(userId)
            }
            else -> error(writeMode)
        }
    }
@@ -151,7 +146,7 @@ class AccessPersistence(
            val state: AccessState?
            synchronized(scheduleLock) {
                pendingMutationTimesMillis -= userId
                state = pendingStates.removeReturnOld(userId)
                state = pendingStates.remove(userId)
                writeHandler.removeMessages(userId)
            }
            if (state == null) {
@@ -201,16 +196,6 @@ class AccessPersistence(
    }

    private inner class WriteHandler(looper: Looper) : Handler(looper) {
        fun writeAtTime(userId: Int, timeMillis: Long) {
            removeMessages(userId)
            val message = obtainMessage(userId)
            sendMessageDelayed(message, timeMillis)
        }

        fun cancelWrite(userId: Int) {
            removeMessages(userId)
        }

        override fun handleMessage(message: Message) {
            val userId = message.what
            writePendingState(userId)
+94 −98
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import com.android.server.SystemConfig
import com.android.server.permission.access.appop.AppIdAppOpPolicy
import com.android.server.permission.access.appop.PackageAppOpPolicy
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.immutable.IndexedMap
import com.android.server.permission.access.permission.AppIdPermissionPolicy
import com.android.server.permission.access.util.attributeInt
import com.android.server.permission.access.util.attributeInterned
@@ -37,14 +39,16 @@ import com.android.server.pm.pkg.PackageState
class AccessPolicy private constructor(
    private val schemePolicies: IndexedMap<String, IndexedMap<String, SchemePolicy>>
) {
    @Suppress("UNCHECKED_CAST")
    constructor() : this(
        IndexedMap<String, IndexedMap<String, SchemePolicy>>().apply {
            fun addPolicy(policy: SchemePolicy) =
                getOrPut(policy.subjectScheme) { IndexedMap() }.put(policy.objectScheme, policy)
        MutableIndexedMap<String, MutableIndexedMap<String, SchemePolicy>>().apply {
            fun addPolicy(policy: SchemePolicy) {
                getOrPut(policy.subjectScheme) { MutableIndexedMap() }[policy.objectScheme] = policy
            }
            addPolicy(AppIdPermissionPolicy())
            addPolicy(AppIdAppOpPolicy())
            addPolicy(PackageAppOpPolicy())
        }
        } as IndexedMap<String, IndexedMap<String, SchemePolicy>>
    )

    fun getSchemePolicy(subjectScheme: String, objectScheme: String): SchemePolicy =
@@ -60,7 +64,7 @@ class AccessPolicy private constructor(
    }

    fun initialize(
        state: AccessState,
        state: MutableAccessState,
        userIds: IntSet,
        packageStates: Map<String, PackageState>,
        disabledSystemPackageStates: Map<String, PackageState>,
@@ -71,25 +75,23 @@ class AccessPolicy private constructor(
        permissionAllowlist: PermissionAllowlist,
        implicitToSourcePermissions: IndexedMap<String, IndexedListSet<String>>
    ) {
        state.systemState.apply {
            this.userIds += userIds
            this.packageStates = packageStates
            this.disabledSystemPackageStates = disabledSystemPackageStates
        state.mutateSystemState(WriteMode.NONE).apply {
            mutateUserIds() += userIds
            setPackageStates(packageStates)
            setDisabledSystemPackageStates(disabledSystemPackageStates)
            packageStates.forEach { (_, packageState) ->
                appIds.getOrPut(packageState.appId) { IndexedListSet() }
                mutateAppIds().mutateOrPut(packageState.appId) { MutableIndexedListSet() }
                    .add(packageState.packageName)
            }
            this.knownPackages = knownPackages
            this.isLeanback = isLeanback
            this.configPermissions = configPermissions
            this.privilegedPermissionAllowlistPackages = privilegedPermissionAllowlistPackages
            this.permissionAllowlist = permissionAllowlist
            this.implicitToSourcePermissions = implicitToSourcePermissions
        }
        state.userStates.apply {
            userIds.forEachIndexed { _, userId ->
                this[userId] = UserState()
            setKnownPackages(knownPackages)
            setLeanback(isLeanback)
            setConfigPermissions(configPermissions)
            setPrivilegedPermissionAllowlistPackages(privilegedPermissionAllowlistPackages)
            setPermissionAllowlist(permissionAllowlist)
            setImplicitToSourcePermissions(implicitToSourcePermissions)
        }
        state.mutateUserStatesNoWrite().apply {
            userIds.forEachIndexed { _, userId -> this[userId] = MutableUserState() }
        }
    }

@@ -106,8 +108,8 @@ class AccessPolicy private constructor(
    }

    fun MutateStateScope.onUserAdded(userId: Int) {
        newState.systemState.userIds += userId
        newState.userStates[userId] = UserState()
        newState.mutateSystemState(WriteMode.NONE).mutateUserIds() += userId
        newState.mutateUserStatesNoWrite()[userId] = MutableUserState()
        forEachSchemePolicy {
            with(it) { onUserAdded(userId) }
        }
@@ -117,8 +119,8 @@ class AccessPolicy private constructor(
    }

    fun MutateStateScope.onUserRemoved(userId: Int) {
        newState.systemState.userIds -= userId
        newState.userStates -= userId
        newState.mutateSystemState(WriteMode.NONE).mutateUserIds() -= userId
        newState.mutateUserStatesNoWrite() -= userId
        forEachSchemePolicy {
            with(it) { onUserRemoved(userId) }
        }
@@ -131,20 +133,20 @@ class AccessPolicy private constructor(
        volumeUuid: String?,
        isSystemUpdated: Boolean
    ) {
        val addedAppIds = IntSet()
        newState.systemState.apply {
            this.packageStates = packageStates
            this.disabledSystemPackageStates = disabledSystemPackageStates
        val addedAppIds = MutableIntSet()
        newState.mutateSystemState(WriteMode.NONE).apply {
            setPackageStates(packageStates)
            setDisabledSystemPackageStates(disabledSystemPackageStates)
            packageStates.forEach { (packageName, packageState) ->
                if (packageState.volumeUuid == volumeUuid) {
                    val appId = packageState.appId
                    appIds.getOrPut(appId) {
                    mutateAppIds().mutateOrPut(appId) {
                        addedAppIds += appId
                        IndexedListSet()
                        MutableIndexedListSet()
                    } += packageName
                }
            }
            this.knownPackages = knownPackages
            setKnownPackages(knownPackages)
        }
        addedAppIds.forEachIndexed { _, appId ->
            forEachSchemePolicy {
@@ -176,14 +178,14 @@ class AccessPolicy private constructor(
        }
        val appId = packageState.appId
        var isAppIdAdded = false
        newState.systemState.apply {
            this.packageStates = packageStates
            this.disabledSystemPackageStates = disabledSystemPackageStates
            appIds.getOrPut(appId) {
        newState.mutateSystemState(WriteMode.NONE).apply {
            setPackageStates(packageStates)
            setDisabledSystemPackageStates(disabledSystemPackageStates)
            mutateAppIds().mutateOrPut(appId) {
                isAppIdAdded = true
                IndexedListSet()
                MutableIndexedListSet()
            } += packageName
            this.knownPackages = knownPackages
            setKnownPackages(knownPackages)
        }
        if (isAppIdAdded) {
            forEachSchemePolicy {
@@ -210,17 +212,17 @@ class AccessPolicy private constructor(
            "Removed package $packageName is still in packageStates in onPackageRemoved()"
        }
        var isAppIdRemoved = false
        newState.systemState.apply {
            this.packageStates = packageStates
            this.disabledSystemPackageStates = disabledSystemPackageStates
            appIds[appId]?.apply {
        newState.mutateSystemState(WriteMode.NONE).apply {
            setPackageStates(packageStates)
            setDisabledSystemPackageStates(disabledSystemPackageStates)
            mutateAppIds().mutate(appId)?.apply {
                this -= packageName
                if (isEmpty()) {
                    appIds -= appId
                    mutateAppIds() -= appId
                    isAppIdRemoved = true
                }
            }
            this.knownPackages = knownPackages
            setKnownPackages(knownPackages)
        }
        forEachSchemePolicy {
            with(it) { onPackageRemoved(packageName, appId) }
@@ -230,9 +232,10 @@ class AccessPolicy private constructor(
                with(it) { onAppIdRemoved(appId) }
            }
        }
        newState.userStates.forEachIndexed { _, _, userState ->
            userState.packageVersions -= packageName
            userState.requestWrite()
        newState.userStates.forEachIndexed { userStateIndex, _, userState ->
            if (packageName in userState.packageVersions) {
                newState.mutateUserStateAt(userStateIndex).mutatePackageVersions() -= packageName
            }
        }
    }

@@ -243,10 +246,10 @@ class AccessPolicy private constructor(
        packageName: String,
        userId: Int
    ) {
        newState.systemState.apply {
            this.packageStates = packageStates
            this.disabledSystemPackageStates = disabledSystemPackageStates
            this.knownPackages = knownPackages
        newState.mutateSystemState(WriteMode.NONE).apply {
            setPackageStates(packageStates)
            setDisabledSystemPackageStates(disabledSystemPackageStates)
            setKnownPackages(knownPackages)
        }
        val packageState = packageStates[packageName]
        // TODO(zhanghai): STOPSHIP: Remove check before feature enable.
@@ -266,10 +269,10 @@ class AccessPolicy private constructor(
        appId: Int,
        userId: Int
    ) {
        newState.systemState.apply {
            this.packageStates = packageStates
            this.disabledSystemPackageStates = disabledSystemPackageStates
            this.knownPackages = knownPackages
        newState.mutateSystemState(WriteMode.NONE).apply {
            setPackageStates(packageStates)
            setDisabledSystemPackageStates(disabledSystemPackageStates)
            setKnownPackages(knownPackages)
        }
        forEachSchemePolicy {
            with(it) { onPackageUninstalled(packageName, appId, userId) }
@@ -277,19 +280,19 @@ class AccessPolicy private constructor(
    }

    fun MutateStateScope.onSystemReady() {
        newState.systemState.isSystemReady = true
        newState.mutateSystemState(WriteMode.NONE).setSystemReady(true)
        forEachSchemePolicy {
            with(it) { onSystemReady() }
        }
    }

    fun migrateSystemState(state: AccessState) {
    fun migrateSystemState(state: MutableAccessState) {
        forEachSchemePolicy {
            with(it) { migrateSystemState(state) }
        }
    }

    fun migrateUserState(state: AccessState, userId: Int) {
    fun migrateUserState(state: MutableAccessState, userId: Int) {
        forEachSchemePolicy {
            with(it) { migrateUserState(state, userId) }
        }
@@ -303,22 +306,17 @@ class AccessPolicy private constructor(
        val packageName = packageState.packageName
        // The version would be latest when the package is new to the system, e.g. newly
        // installed, first boot, or system apps added via OTA.
        val version = newState.userStates[userId].packageVersions[packageName]
        val version = newState.userStates[userId]!!.packageVersions[packageName]
        when {
            version == null -> {
                newState.userStates[userId].apply {
                    packageVersions[packageName] = VERSION_LATEST
                    requestWrite()
                }
            }
            version == null ->
                newState.mutateUserState(userId)!!.mutatePackageVersions()[packageName] =
                    VERSION_LATEST
            version < VERSION_LATEST -> {
                forEachSchemePolicy {
                    with(it) { upgradePackageState(packageState, userId, version) }
                }
                newState.userStates[userId].apply {
                    packageVersions[packageName] = VERSION_LATEST
                    requestWrite()
                }
                newState.mutateUserState(userId)!!.mutatePackageVersions()[packageName] =
                    VERSION_LATEST
            }
            version == VERSION_LATEST -> {}
            else -> Log.w(
@@ -328,7 +326,7 @@ class AccessPolicy private constructor(
        }
    }

    fun BinaryXmlPullParser.parseSystemState(state: AccessState) {
    fun BinaryXmlPullParser.parseSystemState(state: MutableAccessState) {
        forEachTag {
            when (tagName) {
                TAG_ACCESS -> {
@@ -351,7 +349,7 @@ class AccessPolicy private constructor(
        }
    }

    fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {
    fun BinaryXmlPullParser.parseUserState(state: MutableAccessState, userId: Int) {
        forEachTag {
            when (tagName) {
                TAG_ACCESS -> {
@@ -376,33 +374,30 @@ class AccessPolicy private constructor(
        }
    }

    private fun BinaryXmlPullParser.parsePackageVersions(state: AccessState, userId: Int) {
        val userState = state.userStates[userId]
    private fun BinaryXmlPullParser.parsePackageVersions(state: MutableAccessState, userId: Int) {
        val userState = state.mutateUserState(userId, WriteMode.NONE)!!
        val packageVersions = userState.mutatePackageVersions()
        forEachTag {
            when (tagName) {
                TAG_PACKAGE -> parsePackageVersion(userState)
                else -> Log.w(
                    LOG_TAG,
                    "Ignoring unknown tag $name when parsing package versions for user $userId"
                )
                TAG_PACKAGE -> parsePackageVersion(packageVersions)
                else -> Log.w(LOG_TAG, "Ignoring unknown tag $name when parsing package versions")
            }
        }
        userState.packageVersions.retainAllIndexed { _, packageName, _ ->
            val hasPackage = packageName in state.systemState.packageStates
            if (!hasPackage) {
                Log.w(
                    LOG_TAG,
                    "Dropping unknown $packageName when parsing package versions for user $userId"
                )
        packageVersions.forEachReversedIndexed { packageVersionIndex, packageName, _ ->
            if (packageName !in state.systemState.packageStates) {
                Log.w(LOG_TAG, "Dropping unknown $packageName when parsing package versions")
                packageVersions.removeAt(packageVersionIndex)
                userState.requestWriteMode(WriteMode.ASYNCHRONOUS)
            }
            hasPackage
        }
    }

    private fun BinaryXmlPullParser.parsePackageVersion(userState: UserState) {
    private fun BinaryXmlPullParser.parsePackageVersion(
        packageVersions: MutableIndexedMap<String, Int>
    ) {
        val packageName = getAttributeValueOrThrow(ATTR_NAME).intern()
        val version = getAttributeIntOrThrow(ATTR_VERSION)
        userState.packageVersions[packageName] = version
        packageVersions[packageName] = version
    }

    fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
@@ -410,14 +405,15 @@ class AccessPolicy private constructor(
            forEachSchemePolicy {
                with(it) { serializeUserState(state, userId) }
            }

            serializeVersions(state.userStates[userId])
            serializePackageVersions(state.userStates[userId]!!.packageVersions)
        }
    }

    private fun BinaryXmlSerializer.serializeVersions(userState: UserState) {
    private fun BinaryXmlSerializer.serializePackageVersions(
        packageVersions: IndexedMap<String, Int>
    ) {
        tag(TAG_PACKAGE_VERSIONS) {
            userState.packageVersions.forEachIndexed { _, packageName, version ->
            packageVersions.forEachIndexed { _, packageName, version ->
                tag(TAG_PACKAGE) {
                    attributeInterned(ATTR_NAME, packageName)
                    attributeInt(ATTR_VERSION, version)
@@ -430,8 +426,8 @@ class AccessPolicy private constructor(
        getSchemePolicy(subject.scheme, `object`.scheme)

    private inline fun forEachSchemePolicy(action: (SchemePolicy) -> Unit) {
        schemePolicies.forEachValueIndexed { _, objectSchemePolicies ->
            objectSchemePolicies.forEachValueIndexed { _, schemePolicy ->
        schemePolicies.forEachIndexed { _, _, objectSchemePolicies ->
            objectSchemePolicies.forEachIndexed { _, _, schemePolicy ->
                action(schemePolicy)
            }
        }
@@ -491,9 +487,9 @@ abstract class SchemePolicy {

    open fun MutateStateScope.onSystemReady() {}

    open fun migrateSystemState(state: AccessState) {}
    open fun migrateSystemState(state: MutableAccessState) {}

    open fun migrateUserState(state: AccessState, userId: Int) {}
    open fun migrateUserState(state: MutableAccessState, userId: Int) {}

    open fun MutateStateScope.upgradePackageState(
        packageState: PackageState,
@@ -501,11 +497,11 @@ abstract class SchemePolicy {
        version: Int
    ) {}

    open fun BinaryXmlPullParser.parseSystemState(state: AccessState) {}
    open fun BinaryXmlPullParser.parseSystemState(state: MutableAccessState) {}

    open fun BinaryXmlSerializer.serializeSystemState(state: AccessState) {}

    open fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {}
    open fun BinaryXmlPullParser.parseUserState(state: MutableAccessState, userId: Int) {}

    open fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {}
}
+358 −89

File changed.

Preview size limit exceeded, changes collapsed.

+26 −6

File changed.

Preview size limit exceeded, changes collapsed.

Loading