1 /* <lambda>null2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.permission.access 18 19 import android.os.Handler 20 import android.os.Looper 21 import android.os.Message 22 import android.os.SystemClock 23 import android.os.UserHandle 24 import android.util.AtomicFile 25 import android.util.Slog 26 import android.util.SparseLongArray 27 import com.android.internal.annotations.GuardedBy 28 import com.android.modules.utils.BinaryXmlPullParser 29 import com.android.modules.utils.BinaryXmlSerializer 30 import com.android.server.IoThread 31 import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports 32 import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports 33 import com.android.server.permission.access.util.PermissionApex 34 import com.android.server.permission.access.util.parseBinaryXml 35 import com.android.server.permission.access.util.readWithReserveCopy 36 import com.android.server.permission.access.util.serializeBinaryXml 37 import com.android.server.permission.access.util.writeWithReserveCopy 38 import java.io.File 39 import java.io.FileNotFoundException 40 41 class AccessPersistence(private val policy: AccessPolicy) { 42 private val scheduleLock = Any() 43 @GuardedBy("scheduleLock") private val pendingMutationTimesMillis = SparseLongArray() 44 @GuardedBy("scheduleLock") private val pendingStates = MutableIntMap<AccessState>() 45 @GuardedBy("scheduleLock") private lateinit var writeHandler: WriteHandler 46 47 private val writeLock = Any() 48 49 fun initialize() { 50 writeHandler = WriteHandler(IoThread.getHandler().looper) 51 } 52 53 /** 54 * Reads the state either from the disk or migrate legacy data when the data files are missing. 55 */ 56 fun read(state: MutableAccessState) { 57 readSystemState(state) 58 state.externalState.userIds.forEachIndexed { _, userId -> readUserState(state, userId) } 59 } 60 61 private fun readSystemState(state: MutableAccessState) { 62 val fileExists = 63 systemFile.parse { 64 // This is the canonical way to call an extension function in a different class. 65 // TODO(b/259469752): Use context receiver for this when it becomes stable. 66 with(policy) { parseSystemState(state) } 67 } 68 69 if (!fileExists) { 70 policy.migrateSystemState(state) 71 state.systemState.write(state, UserHandle.USER_ALL) 72 } 73 } 74 75 private fun readUserState(state: MutableAccessState, userId: Int) { 76 val fileExists = 77 getUserFile(userId).parse { with(policy) { parseUserState(state, userId) } } 78 79 if (!fileExists) { 80 policy.migrateUserState(state, userId) 81 state.userStates[userId]!!.write(state, userId) 82 } 83 } 84 85 /** 86 * @return {@code true} if the file is successfully read from the disk; {@code false} if the 87 * file doesn't exist yet. 88 */ 89 private inline fun File.parse(block: BinaryXmlPullParser.() -> Unit): Boolean = 90 try { 91 AtomicFile(this).readWithReserveCopy { it.parseBinaryXml(block) } 92 true 93 } catch (e: FileNotFoundException) { 94 Slog.i(LOG_TAG, "$this not found") 95 false 96 } catch (e: Exception) { 97 throw IllegalStateException("Failed to read $this", e) 98 } 99 100 fun write(state: AccessState) { 101 state.systemState.write(state, UserHandle.USER_ALL) 102 state.userStates.forEachIndexed { _, userId, userState -> userState.write(state, userId) } 103 } 104 105 private fun WritableState.write(state: AccessState, userId: Int) { 106 when (val writeMode = writeMode) { 107 WriteMode.NONE -> {} 108 WriteMode.ASYNCHRONOUS -> { 109 synchronized(scheduleLock) { 110 writeHandler.removeMessages(userId) 111 pendingStates[userId] = state 112 // SystemClock.uptimeMillis() is used in Handler.sendMessageDelayed(). 113 val currentTimeMillis = SystemClock.uptimeMillis() 114 val pendingMutationTimeMillis = 115 pendingMutationTimesMillis.getOrPut(userId) { currentTimeMillis } 116 val currentDelayMillis = currentTimeMillis - pendingMutationTimeMillis 117 val message = writeHandler.obtainMessage(userId) 118 if (currentDelayMillis > MAX_WRITE_DELAY_MILLIS) { 119 message.sendToTarget() 120 } else { 121 val newDelayMillis = 122 WRITE_DELAY_TIME_MILLIS.coerceAtMost( 123 MAX_WRITE_DELAY_MILLIS - currentDelayMillis 124 ) 125 writeHandler.sendMessageDelayed(message, newDelayMillis) 126 } 127 } 128 } 129 WriteMode.SYNCHRONOUS -> { 130 synchronized(scheduleLock) { pendingStates[userId] = state } 131 writePendingState(userId) 132 } 133 else -> error(writeMode) 134 } 135 } 136 137 private fun writePendingState(userId: Int) { 138 synchronized(writeLock) { 139 val state: AccessState? 140 synchronized(scheduleLock) { 141 pendingMutationTimesMillis -= userId 142 state = pendingStates.remove(userId) 143 writeHandler.removeMessages(userId) 144 } 145 if (state == null) { 146 return 147 } 148 if (userId == UserHandle.USER_ALL) { 149 writeSystemState(state) 150 } else { 151 writeUserState(state, userId) 152 } 153 } 154 } 155 156 private fun writeSystemState(state: AccessState) { 157 systemFile.serialize { with(policy) { serializeSystemState(state) } } 158 } 159 160 private fun writeUserState(state: AccessState, userId: Int) { 161 getUserFile(userId).serialize { with(policy) { serializeUserState(state, userId) } } 162 } 163 164 private inline fun File.serialize(block: BinaryXmlSerializer.() -> Unit) { 165 try { 166 AtomicFile(this).writeWithReserveCopy { it.serializeBinaryXml(block) } 167 } catch (e: Exception) { 168 Slog.e(LOG_TAG, "Failed to serialize $this", e) 169 } 170 } 171 172 private val systemFile: File 173 get() = File(PermissionApex.systemDataDirectory, FILE_NAME) 174 175 private fun getUserFile(userId: Int): File = 176 File(PermissionApex.getUserDataDirectory(userId), FILE_NAME) 177 178 companion object { 179 private val LOG_TAG = AccessPersistence::class.java.simpleName 180 181 private const val FILE_NAME = "access.abx" 182 183 private const val WRITE_DELAY_TIME_MILLIS = 1000L 184 private const val MAX_WRITE_DELAY_MILLIS = 2000L 185 } 186 187 private inner class WriteHandler(looper: Looper) : Handler(looper) { 188 override fun handleMessage(message: Message) { 189 val userId = message.what 190 writePendingState(userId) 191 } 192 } 193 } 194