1 /* <lambda>null2 * Copyright (C) 2023 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 package com.android.wm.shell.common.pip 17 18 import android.app.AppOpsManager 19 import android.content.ComponentName 20 import android.content.Context 21 import android.content.pm.PackageManager 22 import android.util.Pair 23 import com.android.internal.annotations.VisibleForTesting 24 import com.android.wm.shell.common.ShellExecutor 25 26 class PipAppOpsListener( 27 private val mContext: Context, 28 private val mMainExecutor: ShellExecutor 29 ) { 30 private val mAppOpsManager: AppOpsManager = checkNotNull( 31 mContext.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager) 32 private var mTopPipActivityInfoSupplier: (Context) -> Pair<ComponentName?, Int> = 33 PipUtils::getTopPipActivity 34 private val mAppOpsChangedListener = AppOpsManager.OnOpChangedListener { _, packageName -> 35 try { 36 // Dismiss the PiP once the user disables the app ops setting for that package 37 val topPipActivityInfo = mTopPipActivityInfoSupplier.invoke(mContext) 38 val componentName = topPipActivityInfo.first ?: return@OnOpChangedListener 39 val userId = topPipActivityInfo.second 40 val appInfo = mContext.packageManager 41 .getApplicationInfoAsUser(packageName, 0, userId) 42 if (appInfo.packageName == componentName.packageName && 43 mAppOpsManager.checkOpNoThrow( 44 AppOpsManager.OP_PICTURE_IN_PICTURE, appInfo.uid, 45 packageName 46 ) != AppOpsManager.MODE_ALLOWED 47 ) { 48 mCallback?.let { 49 mMainExecutor.execute { it.dismissPip() } 50 } 51 } 52 } catch (e: PackageManager.NameNotFoundException) { 53 // Unregister the listener if the package can't be found 54 unregisterAppOpsListener() 55 } 56 } 57 58 private var mCallback: Callback? = null 59 60 fun setCallback(callback: Callback) { 61 mCallback = callback 62 } 63 64 fun onActivityPinned(packageName: String) { 65 // Register for changes to the app ops setting for this package while it is in PiP 66 registerAppOpsListener(packageName) 67 } 68 69 fun onActivityUnpinned() { 70 // Unregister for changes to the previously PiP'ed package 71 unregisterAppOpsListener() 72 } 73 74 private fun registerAppOpsListener(packageName: String) { 75 mAppOpsManager.startWatchingMode( 76 AppOpsManager.OP_PICTURE_IN_PICTURE, packageName, 77 mAppOpsChangedListener 78 ) 79 } 80 81 private fun unregisterAppOpsListener() { 82 mAppOpsManager.stopWatchingMode(mAppOpsChangedListener) 83 } 84 85 /** Callback for PipAppOpsListener to request changes to the PIP window. */ 86 interface Callback { 87 /** Dismisses the PIP window. */ 88 fun dismissPip() 89 } 90 91 @VisibleForTesting 92 fun setTopPipActivityInfoSupplier(supplier: (Context) -> Pair<ComponentName?, Int>) { 93 mTopPipActivityInfoSupplier = supplier 94 } 95 }