1 /* 2 * Copyright (C) 2022 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.appop 18 19 import android.Manifest 20 import android.annotation.UserIdInt 21 import android.app.AppGlobals 22 import android.app.AppOpsManager 23 import android.content.pm.PackageManager 24 import android.os.Binder 25 import android.os.Handler 26 import android.os.RemoteException 27 import android.os.UserHandle 28 import android.util.SparseBooleanArray 29 import android.util.SparseIntArray 30 import com.android.internal.annotations.VisibleForTesting 31 import com.android.internal.util.ArrayUtils 32 import com.android.internal.util.function.pooled.PooledLambda 33 import com.android.server.appop.AppOpsCheckingServiceInterface 34 import com.android.server.appop.OnOpModeChangedListener 35 import com.android.server.permission.access.AccessCheckingService 36 import com.android.server.permission.access.AppOpUri 37 import com.android.server.permission.access.PackageUri 38 import com.android.server.permission.access.UidUri 39 import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports 40 import com.android.server.permission.access.util.hasBits 41 import libcore.util.EmptyArray 42 import java.io.PrintWriter 43 44 class AppOpService( 45 private val service: AccessCheckingService 46 ) : AppOpsCheckingServiceInterface { 47 private val packagePolicy = service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME) 48 as PackageAppOpPolicy 49 private val uidPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) 50 as UidAppOpPolicy 51 52 private val context = service.context 53 private lateinit var handler: Handler 54 private lateinit var lock: Any 55 private lateinit var switchedOps: IntMap<IntArray> 56 57 fun initialize() { 58 // TODO(b/252883039): Wrong handler. Inject main thread handler here. 59 handler = Handler(context.mainLooper) 60 // TODO(b/252883039): Wrong lock object. Inject AppOpsService here. 61 lock = Any() 62 63 switchedOps = IntMap() 64 for (switchedCode in 0 until AppOpsManager._NUM_OP) { 65 val switchCode = AppOpsManager.opToSwitch(switchedCode) 66 switchedOps.put(switchCode, 67 ArrayUtils.appendInt(switchedOps.get(switchCode), switchedCode)) 68 } 69 } 70 71 @VisibleForTesting 72 override fun writeState() { 73 // TODO Not yet implemented 74 } 75 76 override fun readState() { 77 // TODO Not yet implemented 78 } 79 80 @VisibleForTesting 81 override fun shutdown() { 82 // TODO Not yet implemented 83 } 84 85 override fun systemReady() { 86 // TODO Not yet implemented 87 } 88 89 override fun getNonDefaultUidModes(uid: Int): SparseIntArray { 90 return opNameMapToOpIntMap(getUidModes(uid)) 91 } 92 93 override fun getNonDefaultPackageModes(packageName: String, userId: Int): SparseIntArray { 94 return opNameMapToOpIntMap(getPackageModes(packageName, userId)) 95 } 96 97 override fun getUidMode(uid: Int, op: Int): Int { 98 val appId = UserHandle.getAppId(uid) 99 val userId = UserHandle.getUserId(uid) 100 val opName = AppOpsManager.opToPublicName(op) 101 return service.getState { 102 with(uidPolicy) { getAppOpMode(appId, userId, opName) } 103 } 104 } 105 106 private fun getUidModes(uid: Int): IndexedMap<String, Int>? { 107 val appId = UserHandle.getAppId(uid) 108 val userId = UserHandle.getUserId(uid) 109 return service.getState { 110 with(uidPolicy) { getAppOpModes(appId, userId) } 111 } 112 } 113 114 override fun setUidMode(uid: Int, op: Int, mode: Int): Boolean { 115 val appId = UserHandle.getAppId(uid) 116 val userId = UserHandle.getUserId(uid) 117 val opName = AppOpsManager.opToPublicName(op) 118 var wasChanged = false 119 service.mutateState { 120 wasChanged = with(uidPolicy) { setAppOpMode(appId, userId, opName, mode) } 121 } 122 return wasChanged 123 } 124 125 override fun getPackageMode(packageName: String, op: Int, userId: Int): Int { 126 val opName = AppOpsManager.opToPublicName(op) 127 return service.getState { 128 with(packagePolicy) { getAppOpMode(packageName, userId, opName) } 129 } 130 } 131 132 private fun getPackageModes( 133 packageName: String, 134 userId: Int 135 ): IndexedMap<String, Int>? = 136 service.getState { with(packagePolicy) { getAppOpModes(packageName, userId) } } 137 138 override fun setPackageMode(packageName: String, op: Int, mode: Int, userId: Int) { 139 val opName = AppOpsManager.opToPublicName(op) 140 service.mutateState { 141 with(packagePolicy) { setAppOpMode(packageName, userId, opName, mode) } 142 } 143 } 144 145 override fun removeUid(uid: Int) { 146 val appId = UserHandle.getAppId(uid) 147 val userId = UserHandle.getUserId(uid) 148 service.mutateState { 149 with(uidPolicy) { removeAppOpModes(appId, userId) } 150 } 151 } 152 153 override fun removePackage(packageName: String, userId: Int): Boolean { 154 var wasChanged = false 155 service.mutateState { 156 wasChanged = with (packagePolicy) { removeAppOpModes(packageName, userId) } 157 } 158 return wasChanged 159 } 160 161 private fun opNameMapToOpIntMap(modes: IndexedMap<String, Int>?): SparseIntArray = 162 if (modes == null) { 163 SparseIntArray() 164 } else { 165 val opIntMap = SparseIntArray(modes.size) 166 modes.forEachIndexed { _, opName, opMode -> 167 opIntMap.put(AppOpsManager.strOpToOp(opName), opMode) 168 } 169 opIntMap 170 } 171 172 override fun areUidModesDefault(uid: Int): Boolean { 173 val modes = getUidModes(uid) 174 return modes == null || modes.isEmpty() 175 } 176 177 override fun arePackageModesDefault(packageName: String, userId: Int): Boolean { 178 val modes = service.getState { getPackageModes(packageName, userId) } 179 return modes == null || modes.isEmpty() 180 } 181 182 override fun clearAllModes() { 183 // We don't need to implement this because it's only called in AppOpsService#readState 184 // and we have our own persistence. 185 } 186 187 // code -> listeners 188 private val opModeWatchers = IntMap<IndexedSet<OnOpModeChangedListener>>() 189 190 // packageName -> listeners 191 private val packageModeWatchers = IndexedMap<String, IndexedSet<OnOpModeChangedListener>>() 192 193 override fun startWatchingOpModeChanged(changedListener: OnOpModeChangedListener, op: Int) { 194 synchronized(lock) { 195 opModeWatchers.getOrPut(op) { IndexedSet() } += changedListener 196 } 197 } 198 199 override fun startWatchingPackageModeChanged( 200 changedListener: OnOpModeChangedListener, 201 packageName: String 202 ) { 203 synchronized(lock) { 204 packageModeWatchers.getOrPut(packageName) { IndexedSet() } += changedListener 205 } 206 } 207 208 override fun removeListener(changedListener: OnOpModeChangedListener) { 209 synchronized(lock) { 210 opModeWatchers.removeAllIndexed { _, _, listeners -> 211 listeners -= changedListener 212 listeners.isEmpty() 213 } 214 packageModeWatchers.removeAllIndexed { _, _, listeners -> 215 listeners -= changedListener 216 listeners.isEmpty() 217 } 218 } 219 } 220 221 override fun getOpModeChangedListeners(op: Int): IndexedSet<OnOpModeChangedListener> { 222 synchronized(lock) { 223 val listeners = opModeWatchers[op] 224 return if (listeners == null) { 225 IndexedSet() 226 } else { 227 IndexedSet(listeners) 228 } 229 } 230 } 231 232 override fun getPackageModeChangedListeners( 233 packageName: String 234 ): IndexedSet<OnOpModeChangedListener> { 235 synchronized(lock) { 236 val listeners = packageModeWatchers[packageName] 237 return if (listeners == null) { 238 IndexedSet() 239 } else { 240 IndexedSet(listeners) 241 } 242 } 243 } 244 245 override fun notifyWatchersOfChange(op: Int, uid: Int) { 246 val listeners = getOpModeChangedListeners(op) 247 listeners.forEachIndexed { _, listener -> 248 notifyOpChanged(listener, op, uid, null) 249 } 250 } 251 252 override fun notifyOpChanged( 253 changedListener: OnOpModeChangedListener, 254 op: Int, 255 uid: Int, 256 packageName: String? 257 ) { 258 if (uid != UID_ANY && 259 changedListener.watchingUid >= 0 && 260 changedListener.watchingUid != uid 261 ) { 262 return 263 } 264 265 // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE 266 val switchedCodes = when (changedListener.watchedOpCode) { 267 ALL_OPS -> switchedOps.get(op) 268 AppOpsManager.OP_NONE -> intArrayOf(op) 269 else -> intArrayOf(changedListener.watchedOpCode) 270 } 271 272 for (switchedCode in switchedCodes) { 273 // There are features watching for mode changes such as window manager 274 // and location manager which are in our process. The callbacks in these 275 // features may require permissions our remote caller does not have. 276 val identity = Binder.clearCallingIdentity() 277 try { 278 if (!shouldIgnoreCallback(switchedCode, changedListener)) { 279 changedListener.onOpModeChanged(switchedCode, uid, packageName) 280 } 281 } catch (e: RemoteException) { 282 /* ignore */ 283 } finally { 284 Binder.restoreCallingIdentity(identity) 285 } 286 } 287 } 288 289 private fun shouldIgnoreCallback(op: Int, listener: OnOpModeChangedListener): Boolean { 290 // If it's a restricted read op, ignore it if watcher doesn't have manage ops permission, 291 // as watcher should not use this to signal if the value is changed. 292 return AppOpsManager.opRestrictsRead(op) && context.checkPermission( 293 Manifest.permission.MANAGE_APPOPS, 294 listener.callingPid, 295 listener.callingUid 296 ) != PackageManager.PERMISSION_GRANTED 297 } 298 299 /** 300 * Construct a map from each listener (listening to the given op, uid) to all of its associated 301 * packageNames (by reverse-indexing opModeWatchers and packageModeWatchers), then invoke 302 * notifyOpChanged for each listener. 303 */ 304 override fun notifyOpChangedForAllPkgsInUid( 305 op: Int, 306 uid: Int, 307 onlyForeground: Boolean, 308 callbackToIgnore: OnOpModeChangedListener? 309 ) { 310 val uidPackageNames = getPackagesForUid(uid) 311 val callbackSpecs = IndexedMap<OnOpModeChangedListener, IndexedSet<String>>() 312 313 fun associateListenerWithPackageNames( 314 listener: OnOpModeChangedListener, 315 packageNames: Array<String> 316 ) { 317 val listenerIsForeground = 318 listener.flags.hasBits(AppOpsManager.WATCH_FOREGROUND_CHANGES) 319 if (onlyForeground && !listenerIsForeground) { 320 return 321 } 322 val changedPackages = callbackSpecs.getOrPut(listener) { IndexedSet() } 323 changedPackages.addAll(packageNames) 324 } 325 326 synchronized(lock) { 327 // Collect all listeners from opModeWatchers and pckageModeWatchers 328 val listeners = opModeWatchers[op] 329 listeners?.forEachIndexed { _, listener -> 330 associateListenerWithPackageNames(listener, uidPackageNames) 331 } 332 uidPackageNames.forEachIndexed { _, uidPackageName -> 333 val packageListeners = packageModeWatchers[uidPackageName] 334 packageListeners?.forEachIndexed { _, listener -> 335 associateListenerWithPackageNames(listener, arrayOf(uidPackageName)) 336 } 337 } 338 // Remove ignored listeners 339 if (callbackToIgnore != null) { 340 callbackSpecs.remove(callbackToIgnore) 341 } 342 } 343 344 // For each (listener, packageName) pair, invoke notifyOpChanged 345 callbackSpecs.forEachIndexed { _, listener, reportedPackageNames -> 346 reportedPackageNames.forEachIndexed { _, reportedPackageName -> 347 handler.sendMessage( 348 PooledLambda.obtainMessage( 349 AppOpService::notifyOpChanged, this, listener, 350 op, uid, reportedPackageName 351 ) 352 ) 353 } 354 } 355 } 356 357 private fun getPackagesForUid(uid: Int): Array<String> { 358 // Very early during boot the package manager is not yet or not yet fully started. At this 359 // time there are no packages yet. 360 return try { 361 AppGlobals.getPackageManager()?.getPackagesForUid(uid) ?: EmptyArray.STRING 362 } catch (e: RemoteException) { 363 EmptyArray.STRING 364 } 365 } 366 367 override fun evalForegroundUidOps( 368 uid: Int, 369 foregroundOps: SparseBooleanArray? 370 ): SparseBooleanArray? { 371 synchronized(lock) { 372 val uidModes = getUidModes(uid) 373 return evalForegroundOps(uidModes, foregroundOps) 374 } 375 } 376 377 override fun evalForegroundPackageOps( 378 packageName: String, 379 foregroundOps: SparseBooleanArray?, 380 @UserIdInt userId: Int 381 ): SparseBooleanArray? { 382 synchronized(lock) { 383 val ops = service.getState { getPackageModes(packageName, userId) } 384 return evalForegroundOps(ops, foregroundOps) 385 } 386 } 387 388 private fun evalForegroundOps( 389 ops: IndexedMap<String, Int>?, 390 foregroundOps: SparseBooleanArray? 391 ): SparseBooleanArray? { 392 var foregroundOps = foregroundOps 393 ops?.forEachIndexed { _, opName, opMode -> 394 if (opMode == AppOpsManager.MODE_FOREGROUND) { 395 if (foregroundOps == null) { 396 foregroundOps = SparseBooleanArray() 397 } 398 evalForegroundWatchers(opName, foregroundOps!!) 399 } 400 } 401 return foregroundOps 402 } 403 404 private fun evalForegroundWatchers(opName: String, foregroundOps: SparseBooleanArray) { 405 val opCode = AppOpsManager.strOpToOp(opName) 406 val listeners = opModeWatchers[opCode] 407 val hasForegroundListeners = foregroundOps[opCode] || listeners?.anyIndexed { _, listener -> 408 listener.flags.hasBits(AppOpsManager.WATCH_FOREGROUND_CHANGES) 409 } ?: false 410 foregroundOps.put(opCode, hasForegroundListeners) 411 } 412 413 override fun dumpListeners( 414 dumpOp: Int, 415 dumpUid: Int, 416 dumpPackage: String?, 417 printWriter: PrintWriter 418 ): Boolean { 419 var needSep = false 420 if (opModeWatchers.size() > 0) { 421 var printedHeader = false 422 opModeWatchers.forEachIndexed { _, op, modeChangedListenerSet -> 423 if (dumpOp >= 0 && dumpOp != op) { 424 return@forEachIndexed // continue 425 } 426 val opName = AppOpsManager.opToName(op) 427 var printedOpHeader = false 428 modeChangedListenerSet.forEachIndexed listenerLoop@ { listenerIndex, listener -> 429 with(printWriter) { 430 if (dumpPackage != null && 431 dumpUid != UserHandle.getAppId(listener.watchingUid)) { 432 return@listenerLoop // continue 433 } 434 needSep = true 435 if (!printedHeader) { 436 println(" Op mode watchers:") 437 printedHeader = true 438 } 439 if (!printedOpHeader) { 440 print(" Op ") 441 print(opName) 442 println(":") 443 printedOpHeader = true 444 } 445 print(" #") 446 print(listenerIndex) 447 print(opName) 448 print(": ") 449 println(listener.toString()) 450 } 451 } 452 } 453 } 454 455 if (packageModeWatchers.size > 0 && dumpOp < 0) { 456 var printedHeader = false 457 packageModeWatchers.forEachIndexed { _, packageName, listeners -> 458 with(printWriter) { 459 if (dumpPackage != null && dumpPackage != packageName) { 460 return@forEachIndexed // continue 461 } 462 needSep = true 463 if (!printedHeader) { 464 println(" Package mode watchers:") 465 printedHeader = true 466 } 467 print(" Pkg ") 468 print(packageName) 469 println(":") 470 listeners.forEachIndexed { listenerIndex, listener -> 471 print(" #") 472 print(listenerIndex) 473 print(": ") 474 println(listener.toString()) 475 } 476 } 477 } 478 } 479 return needSep 480 } 481 482 companion object { 483 private val LOG_TAG = AppOpService::class.java.simpleName 484 485 // Constant meaning that any UID should be matched when dispatching callbacks 486 private const val UID_ANY = -2 487 488 // If watchedOpCode==ALL_OPS, notify for ops affected by the switch-op 489 private const val ALL_OPS = -2 490 } 491 } 492