1 /* 2 * 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.Log 26 import com.android.internal.annotations.GuardedBy 27 import com.android.internal.os.BackgroundThread 28 import com.android.modules.utils.BinaryXmlPullParser 29 import com.android.modules.utils.BinaryXmlSerializer 30 import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports 31 import com.android.server.permission.access.util.PermissionApex 32 import com.android.server.permission.access.util.parseBinaryXml 33 import com.android.server.permission.access.util.read 34 import com.android.server.permission.access.util.serializeBinaryXml 35 import com.android.server.permission.access.util.writeInlined 36 import java.io.File 37 import java.io.FileNotFoundException 38 39 class AccessPersistence( 40 private val policy: AccessPolicy 41 ) { 42 private val scheduleLock = Any() 43 @GuardedBy("scheduleLock") 44 private val pendingMutationTimesMillis = IntLongMap() 45 @GuardedBy("scheduleLock") 46 private val pendingStates = IntMap<AccessState>() 47 @GuardedBy("scheduleLock") 48 private lateinit var writeHandler: WriteHandler 49 50 private val writeLock = Any() 51 52 fun initialize() { 53 writeHandler = WriteHandler(BackgroundThread.getHandler().looper) 54 } 55 56 fun read(state: AccessState) { 57 readSystemState(state) 58 state.systemState.userIds.forEachIndexed { _, userId -> 59 readUserState(state, userId) 60 } 61 } 62 63 private fun readSystemState(state: AccessState) { 64 systemFile.parse { 65 // This is the canonical way to call an extension function in a different class. 66 // TODO(b/259469752): Use context receiver for this when it becomes stable. 67 with(policy) { parseSystemState(state) } 68 } 69 } 70 71 private fun readUserState(state: AccessState, userId: Int) { 72 getUserFile(userId).parse { 73 with(policy) { parseUserState(state, userId) } 74 } 75 } 76 77 private inline fun File.parse(block: BinaryXmlPullParser.() -> Unit) { 78 try { 79 AtomicFile(this).read { it.parseBinaryXml(block) } 80 } catch (e: FileNotFoundException) { 81 Log.i(LOG_TAG, "$this not found") 82 } catch (e: Exception) { 83 throw IllegalStateException("Failed to read $this", e) 84 } 85 } 86 87 fun write(state: AccessState) { 88 state.systemState.write(state, UserHandle.USER_ALL) 89 state.userStates.forEachIndexed { _, userId, userState -> 90 userState.write(state, userId) 91 } 92 } 93 94 private fun WritableState.write(state: AccessState, userId: Int) { 95 when (val writeMode = writeMode) { 96 WriteMode.NONE -> {} 97 WriteMode.SYNC -> { 98 synchronized(scheduleLock) { pendingStates[userId] = state } 99 writePendingState(userId) 100 } 101 WriteMode.ASYNC -> { 102 synchronized(scheduleLock) { 103 writeHandler.removeMessages(userId) 104 pendingStates[userId] = state 105 // SystemClock.uptimeMillis() is used in Handler.sendMessageDelayed(). 106 val currentTimeMillis = SystemClock.uptimeMillis() 107 val pendingMutationTimeMillis = 108 pendingMutationTimesMillis.getOrPut(userId) { currentTimeMillis } 109 val currentDelayMillis = currentTimeMillis - pendingMutationTimeMillis 110 val message = writeHandler.obtainMessage(userId) 111 if (currentDelayMillis > MAX_WRITE_DELAY_MILLIS) { 112 message.sendToTarget() 113 } else { 114 val newDelayMillis = WRITE_DELAY_TIME_MILLIS 115 .coerceAtMost(MAX_WRITE_DELAY_MILLIS - currentDelayMillis) 116 writeHandler.sendMessageDelayed(message, newDelayMillis) 117 } 118 } 119 } 120 else -> error(writeMode) 121 } 122 } 123 124 private fun writePendingState(userId: Int) { 125 synchronized(writeLock) { 126 val state: AccessState? 127 synchronized(scheduleLock) { 128 pendingMutationTimesMillis -= userId 129 state = pendingStates.removeReturnOld(userId) 130 writeHandler.removeMessages(userId) 131 } 132 if (state == null) { 133 return 134 } 135 if (userId == UserHandle.USER_ALL) { 136 writeSystemState(state) 137 } else { 138 writeUserState(state, userId) 139 } 140 } 141 } 142 143 private fun writeSystemState(state: AccessState) { 144 systemFile.serialize { 145 with(policy) { serializeSystemState(state) } 146 } 147 } 148 149 private fun writeUserState(state: AccessState, userId: Int) { 150 getUserFile(userId).serialize { 151 with(policy) { serializeUserState(state, userId) } 152 } 153 } 154 155 private inline fun File.serialize(block: BinaryXmlSerializer.() -> Unit) { 156 try { 157 AtomicFile(this).writeInlined { it.serializeBinaryXml(block) } 158 } catch (e: Exception) { 159 Log.e(LOG_TAG, "Failed to serialize $this", e) 160 } 161 } 162 163 private val systemFile: File 164 get() = File(PermissionApex.systemDataDirectory, FILE_NAME) 165 166 private fun getUserFile(userId: Int): File = 167 File(PermissionApex.getUserDataDirectory(userId), FILE_NAME) 168 169 companion object { 170 private val LOG_TAG = AccessPersistence::class.java.simpleName 171 172 private const val FILE_NAME = "access.abx" 173 174 private const val WRITE_DELAY_TIME_MILLIS = 1000L 175 private const val MAX_WRITE_DELAY_MILLIS = 2000L 176 } 177 178 private inner class WriteHandler(looper: Looper) : Handler(looper) { 179 fun writeAtTime(userId: Int, timeMillis: Long) { 180 removeMessages(userId) 181 val message = obtainMessage(userId) 182 sendMessageDelayed(message, timeMillis) 183 } 184 185 fun cancelWrite(userId: Int) { 186 removeMessages(userId) 187 } 188 189 override fun handleMessage(message: Message) { 190 val userId = message.what 191 writePendingState(userId) 192 } 193 } 194 } 195