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