Support blocking screen on multi-display.
- register UXR listener for each physical display;
- non-physical (likely virtual) displays gets UXR for main display;
- if no display has physical address, assume main display is physical;
Bug: 128456985
Test: verified on device with single display; also verified with
multi-display as 2nd home.
Change-Id: I54b45915e1e32f01613d3477c10b2e8c38e4e94e
diff --git a/service/src/com/android/car/SystemActivityMonitoringService.java b/service/src/com/android/car/SystemActivityMonitoringService.java
index 3894337..d192727 100644
--- a/service/src/com/android/car/SystemActivityMonitoringService.java
+++ b/service/src/com/android/car/SystemActivityMonitoringService.java
@@ -15,6 +15,8 @@
*/
package com.android.car;
+import static com.android.car.pm.CarPackageManagerService.BLOCKING_INTENT_EXTRA_DISPLAY_ID;
+
import android.app.ActivityManager;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityOptions;
@@ -104,7 +106,7 @@
private final HandlerThread mMonitorHandlerThread;
private final ActivityMonitorHandler mHandler;
- /** K: stack id, V: top task */
+ /** K: display id, V: top task */
private final SparseArray<TopTaskInfoContainer> mTopTasks = new SparseArray<>();
/** K: uid, V : list of pid */
private final Map<Integer, Set<Integer>> mForegroundUidPids = new ArrayMap<>();
@@ -377,10 +379,14 @@
* block the current task with the provided new activity.
*/
private void handleBlockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent) {
- // Only block default display.
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchDisplayId(Display.DEFAULT_DISPLAY);
+ int displayId = newActivityIntent.getIntExtra(BLOCKING_INTENT_EXTRA_DISPLAY_ID,
+ Display.DEFAULT_DISPLAY);
+ if (Log.isLoggable(CarLog.TAG_AM, Log.DEBUG)) {
+ Log.d(CarLog.TAG_AM, "Launching blocking activity on display: " + displayId);
+ }
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(displayId);
mContext.startActivityAsUser(newActivityIntent, options.toBundle(),
new UserHandle(currentTask.stackInfo.userId));
// Now make stack with new activity focused.
diff --git a/service/src/com/android/car/pm/CarPackageManagerService.java b/service/src/com/android/car/pm/CarPackageManagerService.java
index 7615d28..0f33ff4 100644
--- a/service/src/com/android/car/pm/CarPackageManagerService.java
+++ b/service/src/com/android/car/pm/CarPackageManagerService.java
@@ -42,6 +42,7 @@
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
import android.content.res.Resources;
+import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -54,7 +55,9 @@
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
+import android.util.SparseArray;
import android.view.Display;
+import android.view.DisplayAddress;
import com.android.car.CarLog;
import com.android.car.CarServiceBase;
@@ -89,6 +92,7 @@
private final SystemActivityMonitoringService mSystemActivityMonitoringService;
private final PackageManager mPackageManager;
private final ActivityManager mActivityManager;
+ private final DisplayManager mDisplayManager;
private final HandlerThread mHandlerThread;
private final PackageHandler mHandler;
@@ -110,7 +114,6 @@
private final HashMap<String, ClientPolicy> mClientPolicies = new HashMap<>();
@GuardedBy("this")
private HashMap<String, AppBlockingPackageInfoWrapper> mActivityWhitelistMap = new HashMap<>();
- // The list corresponding to the one configured in <activityBlacklist>
@GuardedBy("this")
private LinkedList<AppBlockingPolicyProxy> mProxies;
@@ -122,7 +125,10 @@
private final ComponentName mActivityBlockingActivity;
private final ActivityLaunchListener mActivityLaunchListener = new ActivityLaunchListener();
- private final UxRestrictionsListener mUxRestrictionsListener;
+ // K: (logical) display id of a physical display, V: UXR change listener of this display.
+ // For multi-display, monitor UXR change on each display.
+ private final SparseArray<UxRestrictionsListener> mUxRestrictionsListeners =
+ new SparseArray<>();
private final VendorServiceController mVendorServiceController;
// Information related to when the installed packages should be parsed for building a white and
@@ -171,6 +177,12 @@
*/
public static final String BLOCKING_INTENT_EXTRA_IS_ROOT_ACTIVITY_DO = "is_root_activity_do";
+ /**
+ * int display id of the blocked task.
+ * @hide
+ */
+ public static final String BLOCKING_INTENT_EXTRA_DISPLAY_ID = "display_id";
+
public CarPackageManagerService(Context context,
CarUxRestrictionsManagerService uxRestrictionsService,
SystemActivityMonitoringService systemActivityMonitoringService,
@@ -180,7 +192,7 @@
mSystemActivityMonitoringService = systemActivityMonitoringService;
mPackageManager = mContext.getPackageManager();
mActivityManager = mContext.getSystemService(ActivityManager.class);
- mUxRestrictionsListener = new UxRestrictionsListener(uxRestrictionsService);
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
mHandlerThread = new HandlerThread(CarLog.TAG_PACKAGE);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
@@ -281,14 +293,14 @@
@Override
public boolean isActivityBackedBySafeActivity(ComponentName activityName) {
- if (!mUxRestrictionsListener.isRestricted()) {
- return true;
- }
StackInfo info = mSystemActivityMonitoringService.getFocusedStackForTopActivity(
activityName);
if (info == null) { // not top in focused stack
return true;
}
+ if (!isUxRestrictedOnDisplay(info.displayId)) {
+ return true;
+ }
if (info.taskNames.length <= 1) { // nothing below this.
return false;
}
@@ -385,8 +397,11 @@
}
mContext.unregisterReceiver(mPackageParsingEventReceiver);
mContext.unregisterReceiver(mUserSwitchedEventReceiver);
- mCarUxRestrictionsService.unregisterUxRestrictionsChangeListener(mUxRestrictionsListener);
mSystemActivityMonitoringService.registerActivityLaunchListener(null);
+ for (int i = 0; i < mUxRestrictionsListeners.size(); i++) {
+ UxRestrictionsListener listener = mUxRestrictionsListeners.valueAt(i);
+ mCarUxRestrictionsService.unregisterUxRestrictionsChangeListener(listener);
+ }
}
// run from HandlerThread
@@ -402,15 +417,22 @@
pkgParseIntent.addDataScheme("package");
mContext.registerReceiverAsUser(mPackageParsingEventReceiver, UserHandle.ALL,
pkgParseIntent, null, null);
- try {
- // TODO(128456985): register listener for each display in order to
- // properly launch blocking screens.
- mCarUxRestrictionsService.registerUxRestrictionsChangeListener(
- mUxRestrictionsListener, Display.DEFAULT_DISPLAY);
- } catch (IllegalArgumentException e) {
- // can happen while mocking is going on while init is still done.
- Log.w(CarLog.TAG_PACKAGE, "sensor subscription failed", e);
- return;
+
+ List<Display> physicalDisplays = getPhysicalDisplays();
+
+ // Assume default display (display 0) is always a physical display.
+ Display defaultDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ if (!physicalDisplays.contains(defaultDisplay)) {
+ if (Log.isLoggable(CarLog.TAG_PACKAGE, Log.INFO)) {
+ Log.i(CarLog.TAG_PACKAGE, "Adding default display to physical displays.");
+ }
+ physicalDisplays.add(defaultDisplay);
+ }
+ for (Display physicalDisplay : physicalDisplays) {
+ int displayId = physicalDisplay.getDisplayId();
+ UxRestrictionsListener listener = new UxRestrictionsListener(mCarUxRestrictionsService);
+ mUxRestrictionsListeners.put(displayId, listener);
+ mCarUxRestrictionsService.registerUxRestrictionsChangeListener(listener, displayId);
}
mSystemActivityMonitoringService.registerActivityLaunchListener(
mActivityLaunchListener);
@@ -423,7 +445,7 @@
synchronized (this) {
mHasParsedPackages = true;
}
- mUxRestrictionsListener.checkIfTopActivityNeedsBlocking();
+ blockTopActivitiesIfNecessary();
}
private synchronized void doHandleRelease() {
@@ -901,7 +923,14 @@
writer.println("*PackageManagementService*");
writer.println("mEnableActivityBlocking:" + mEnableActivityBlocking);
writer.println("mHasParsedPackages:" + mHasParsedPackages);
- writer.println("ActivityRestricted:" + mUxRestrictionsListener.isRestricted());
+ List<String> restrictions = new ArrayList<>(mUxRestrictionsListeners.size());
+ for (int i = 0; i < mUxRestrictionsListeners.size(); i++) {
+ int displayId = mUxRestrictionsListeners.keyAt(i);
+ UxRestrictionsListener listener = mUxRestrictionsListeners.valueAt(i);
+ restrictions.add(String.format("Display %d is %s",
+ displayId, (listener.isRestricted() ? "restricted" : "unrestricted")));
+ }
+ writer.println("Display Restrictions:\n" + String.join("\n", restrictions));
writer.println(String.join("\n", mBlockedActivityLogs));
writer.print(dumpPoliciesLocked(true));
}
@@ -946,15 +975,55 @@
return sb.toString();
}
+ /**
+ * Returns display with physical address.
+ */
+ private List<Display> getPhysicalDisplays() {
+ List<Display> displays = new ArrayList<>();
+ for (Display display : mDisplayManager.getDisplays()) {
+ if (display.getAddress() instanceof DisplayAddress.Physical) {
+ displays.add(display);
+ }
+ }
+ return displays;
+ }
+
+ /**
+ * Returns whether UX restrictions is required for display.
+ *
+ * Non-physical display will use restrictions for {@link Display#DEFAULT_DISPLAY}.
+ */
+ private boolean isUxRestrictedOnDisplay(int displayId) {
+ UxRestrictionsListener listenerForTopTaskDisplay;
+ if (mUxRestrictionsListeners.indexOfKey(displayId) < 0) {
+ listenerForTopTaskDisplay = mUxRestrictionsListeners.get(Display.DEFAULT_DISPLAY);
+ if (listenerForTopTaskDisplay == null) {
+ // This should never happen.
+ Log.e(CarLog.TAG_PACKAGE, "Missing listener for default display.");
+ return true;
+ }
+ } else {
+ listenerForTopTaskDisplay = mUxRestrictionsListeners.get(displayId);
+ }
+
+ return listenerForTopTaskDisplay.isRestricted();
+ }
+
+ private void blockTopActivitiesIfNecessary() {
+ List<TopTaskInfoContainer> topTasks = mSystemActivityMonitoringService.getTopTasks();
+ for (TopTaskInfoContainer topTask : topTasks) {
+ if (topTask == null) {
+ Log.e(CarLog.TAG_PACKAGE, "Top tasks contains null.");
+ continue;
+ }
+ blockTopActivityIfNecessary(topTask);
+ }
+ }
+
private void blockTopActivityIfNecessary(TopTaskInfoContainer topTask) {
- // Only block activities launched on default display.
- if (topTask.displayId != Display.DEFAULT_DISPLAY) {
- return;
+ if (isUxRestrictedOnDisplay(topTask.displayId)) {
+ doBlockTopActivityIfNotAllowed(topTask);
}
- if (!mUxRestrictionsListener.isRestricted()) {
- return;
- }
- doBlockTopActivityIfNotAllowed(topTask);
}
private void doBlockTopActivityIfNotAllowed(TopTaskInfoContainer topTask) {
@@ -1004,8 +1073,9 @@
}
Intent newActivityIntent = createBlockingActivityIntent(
- mActivityBlockingActivity, topTask.topActivity.flattenToShortString(),
- topTask.taskId, taskRootActivity, isRootDO);
+ mActivityBlockingActivity, topTask.displayId,
+ topTask.topActivity.flattenToShortString(), topTask.taskId, taskRootActivity,
+ isRootDO);
// Intent contains all info to debug what is blocked - log into both logcat and dumpsys.
String log = "Starting blocking activity with intent: " + newActivityIntent.toUri(0);
@@ -1027,10 +1097,14 @@
* @return an intent to launch the blocking activity.
*/
private static Intent createBlockingActivityIntent(ComponentName blockingActivity,
- String blockedActivity, int blockedTaskId, String taskRootActivity, boolean isRootDo) {
+ int displayId, String blockedActivity, int blockedTaskId, String taskRootActivity,
+ boolean isRootDo) {
Intent newActivityIntent = new Intent();
+ newActivityIntent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
newActivityIntent.setComponent(blockingActivity);
newActivityIntent.putExtra(
+ BLOCKING_INTENT_EXTRA_DISPLAY_ID, displayId);
+ newActivityIntent.putExtra(
BLOCKING_INTENT_EXTRA_BLOCKED_ACTIVITY_NAME, blockedActivity);
newActivityIntent.putExtra(
BLOCKING_INTENT_EXTRA_BLOCKED_TASK_ID, blockedTaskId);
@@ -1042,21 +1116,6 @@
return newActivityIntent;
}
- private void blockTopActivitiesIfNecessary() {
- boolean restricted = mUxRestrictionsListener.isRestricted();
- if (!restricted) {
- return;
- }
- List<TopTaskInfoContainer> topTasks = mSystemActivityMonitoringService.getTopTasks();
- for (TopTaskInfoContainer topTask : topTasks) {
- if (topTask == null) {
- Log.e(CarLog.TAG_PACKAGE, "Top tasks contains null.");
- continue;
- }
- doBlockTopActivityIfNotAllowed(topTask);
- }
- }
-
/**
* Enable/Disable activity blocking by correspondingly enabling/disabling broadcasting UXR
* changes in {@link CarUxRestrictionsManagerService}. This is only available in
@@ -1327,9 +1386,10 @@
}
}
if (DBG_POLICY_ENFORCEMENT) {
- Log.d(CarLog.TAG_PACKAGE, "block?: " + shouldCheck);
+ Log.d(CarLog.TAG_PACKAGE, "Should check top tasks?: " + shouldCheck);
}
if (shouldCheck) {
+ // Loop over all top tasks to ensure tasks on virtual display can also be blocked.
blockTopActivitiesIfNecessary();
}
}