diff --git a/api/current.txt b/api/current.txt index c061bcd7ef6ac..7f8760897f7f6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4167,6 +4167,7 @@ package android.app { field public static final java.lang.String OPSTR_MOCK_LOCATION = "android:mock_location"; field public static final java.lang.String OPSTR_MONITOR_HIGH_POWER_LOCATION = "android:monitor_location_high_power"; field public static final java.lang.String OPSTR_MONITOR_LOCATION = "android:monitor_location"; + field public static final java.lang.String OPSTR_PICTURE_IN_PICTURE = "android:picture_in_picture"; field public static final java.lang.String OPSTR_PROCESS_OUTGOING_CALLS = "android:process_outgoing_calls"; field public static final java.lang.String OPSTR_READ_CALENDAR = "android:read_calendar"; field public static final java.lang.String OPSTR_READ_CALL_LOG = "android:read_call_log"; diff --git a/api/system-current.txt b/api/system-current.txt index a1d7d915f5bcd..cd945e1f9c44b 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4310,6 +4310,7 @@ package android.app { field public static final java.lang.String OPSTR_MOCK_LOCATION = "android:mock_location"; field public static final java.lang.String OPSTR_MONITOR_HIGH_POWER_LOCATION = "android:monitor_location_high_power"; field public static final java.lang.String OPSTR_MONITOR_LOCATION = "android:monitor_location"; + field public static final java.lang.String OPSTR_PICTURE_IN_PICTURE = "android:picture_in_picture"; field public static final java.lang.String OPSTR_PROCESS_OUTGOING_CALLS = "android:process_outgoing_calls"; field public static final java.lang.String OPSTR_READ_CALENDAR = "android:read_calendar"; field public static final java.lang.String OPSTR_READ_CALL_LOG = "android:read_call_log"; diff --git a/api/test-current.txt b/api/test-current.txt index 4b2d6eb059acb..a80e7d9ba8019 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -4177,6 +4177,7 @@ package android.app { field public static final java.lang.String OPSTR_MOCK_LOCATION = "android:mock_location"; field public static final java.lang.String OPSTR_MONITOR_HIGH_POWER_LOCATION = "android:monitor_location_high_power"; field public static final java.lang.String OPSTR_MONITOR_LOCATION = "android:monitor_location"; + field public static final java.lang.String OPSTR_PICTURE_IN_PICTURE = "android:picture_in_picture"; field public static final java.lang.String OPSTR_PROCESS_OUTGOING_CALLS = "android:process_outgoing_calls"; field public static final java.lang.String OPSTR_READ_CALENDAR = "android:read_calendar"; field public static final java.lang.String OPSTR_READ_CALL_LOG = "android:read_call_log"; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 09e7595242aff..cbd7b9d4aa9cf 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -245,8 +245,8 @@ public class AppOpsManager { public static final int OP_READ_PHONE_NUMBER = 65; /** @hide Request package installs through package installer */ public static final int OP_REQUEST_INSTALL_PACKAGES = 66; - /** @hide Enter picture-in-picture when hidden. */ - public static final int OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE = 67; + /** @hide Enter picture-in-picture. */ + public static final int OP_PICTURE_IN_PICTURE = 67; /** @hide Instant app start foreground service. */ public static final int OP_INSTANT_APP_START_FOREGROUND = 68; /** @hide Answer incoming phone calls */ @@ -355,6 +355,9 @@ public class AppOpsManager { = "android:get_accounts"; public static final String OPSTR_READ_PHONE_NUMBER = "android:read_phone_number"; + /** Access to picture-in-picture. */ + public static final String OPSTR_PICTURE_IN_PICTURE + = "android:picture_in_picture"; /** @hide */ public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground"; @@ -486,7 +489,7 @@ public class AppOpsManager { OP_AUDIO_ACCESSIBILITY_VOLUME, OP_READ_PHONE_NUMBER, OP_REQUEST_INSTALL_PACKAGES, - OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE, + OP_PICTURE_IN_PICTURE, OP_INSTANT_APP_START_FOREGROUND, OP_ANSWER_PHONE_CALLS }; @@ -563,7 +566,7 @@ public class AppOpsManager { null, // OP_AUDIO_ACCESSIBILITY_VOLUME OPSTR_READ_PHONE_NUMBER, null, // OP_REQUEST_INSTALL_PACKAGES - null, + OPSTR_PICTURE_IN_PICTURE, OPSTR_INSTANT_APP_START_FOREGROUND, OPSTR_ANSWER_PHONE_CALLS, }; @@ -640,7 +643,7 @@ public class AppOpsManager { "AUDIO_ACCESSIBILITY_VOLUME", "READ_PHONE_NUMBER", "REQUEST_INSTALL_PACKAGES", - "OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE", + "PICTURE_IN_PICTURE", "INSTANT_APP_START_FOREGROUND", "ANSWER_PHONE_CALLS", }; @@ -948,7 +951,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, // OP_AUDIO_ACCESSIBILITY_VOLUME AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_DEFAULT, // OP_REQUEST_INSTALL_PACKAGES - AppOpsManager.MODE_ALLOWED, // OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE + AppOpsManager.MODE_ALLOWED, // OP_PICTURE_IN_PICTURE AppOpsManager.MODE_DEFAULT, // OP_INSTANT_APP_START_FOREGROUND AppOpsManager.MODE_ALLOWED, // ANSWER_PHONE_CALLS }; @@ -1028,7 +1031,7 @@ public class AppOpsManager { false, // OP_AUDIO_ACCESSIBILITY_VOLUME false, false, // OP_REQUEST_INSTALL_PACKAGES - false, // OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE + false, // OP_PICTURE_IN_PICTURE false, false, // ANSWER_PHONE_CALLS }; diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index f06c8dcf887c9..fb714b9b191a9 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -3345,11 +3345,11 @@ message MetricsEvent { // CATEGORY: SETTINGS SETTINGS_MANAGE_PICTURE_IN_PICTURE = 812; - // ACTION: Allow "Enable picture-in-picture on hide" for an app - APP_PICTURE_IN_PICTURE_ON_HIDE_ALLOW = 813; + // ACTION: Allow "Enable picture-in-picture" for an app + APP_PICTURE_IN_PICTURE_ALLOW = 813; - // ACTION: Deny "Enable picture-in-picture on hide" for an app - APP_PICTURE_IN_PICTURE_ON_HIDE_DENY = 814; + // ACTION: Deny "Enable picture-in-picture" for an app + APP_PICTURE_IN_PICTURE_DENY = 814; // OPEN: Settings > Language & input > Text-to-speech output -> Speech rate & pitch // CATEGORY: SETTINGS diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 2b2471b28aa57..7868fdfd864a2 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -26,7 +26,7 @@ import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.AppOpsManager.MODE_ALLOWED; -import static android.app.AppOpsManager.OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE; +import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; @@ -1005,6 +1005,11 @@ final class ActivityRecord implements AppWindowContainerListener { * the activity is not currently visible and {@param noThrow} is not set. */ boolean checkEnterPictureInPictureState(String caller, boolean noThrow) { + // Check app-ops and see if PiP is supported for this package + if (!checkEnterPictureInPictureAppOpsState()) { + return false; + } + boolean isCurrentAppLocked = mStackSupervisor.getLockTaskModeState() != LOCK_TASK_MODE_NONE; boolean isKeyguardLocked = service.isKeyguardLocked(); boolean hasPinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID) != null; @@ -1022,15 +1027,13 @@ final class ActivityRecord implements AppWindowContainerListener { // require that there is not an existing PiP activity and that the current system // state supports entering PiP return isNotLockedOrOnKeyguard && !hasPinnedStack - && supportsPictureInPictureWhilePausing - && checkEnterPictureInPictureOnHideAppOpsState(); + && supportsPictureInPictureWhilePausing; case STOPPING: // When stopping in a valid state, then only allow enter PiP as in the pause state. // Otherwise, fall through to throw an exception if the caller is trying to enter // PiP in an invalid stopping state. if (supportsPictureInPictureWhilePausing) { - return isNotLockedOrOnKeyguard && !hasPinnedStack - && checkEnterPictureInPictureOnHideAppOpsState(); + return isNotLockedOrOnKeyguard && !hasPinnedStack; } default: if (noThrow) { @@ -1044,11 +1047,11 @@ final class ActivityRecord implements AppWindowContainerListener { } /** - * @return Whether AppOps allows this package to enter picture-in-picture when it is hidden. + * @return Whether AppOps allows this package to enter picture-in-picture. */ - private boolean checkEnterPictureInPictureOnHideAppOpsState() { + private boolean checkEnterPictureInPictureAppOpsState() { try { - return service.getAppOpsService().checkOperation(OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE, + return service.getAppOpsService().checkOperation(OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) == MODE_ALLOWED; } catch (RemoteException e) { // Local call