Set permissions for launching on private displays
- System UIDs must be allowed to launch anything and everywhere.
- Display owner must be allowed to launch activities on it.
- Apps that are already on target display must be allowed to launch
there.
- All other apps mustn't be allowed to launch on private displays.
Bug: 34230873
Test: android.server.cts.ActivityManagerDisplayTests
Test: #testPermissionLaunchFromSystem
Test: #testPermissionLaunchFromAppOnSecondary
Test: #testPermissionLaunchFromOwner
Test: #testPermissionLaunchFromDifferentApp
Change-Id: Ic98005649a6368370c512e822cba4e9decc18ae9
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index aea1258..3eb5844 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -19,6 +19,8 @@
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.PowerManager;
+import android.util.IntArray;
+import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
@@ -147,6 +149,21 @@
public abstract void setDisplayOffsets(int displayId, int x, int y);
/**
+ * Provide a list of UIDs that are present on the display and are allowed to access it.
+ *
+ * @param displayAccessUIDs Mapping displayId -> int array of UIDs.
+ */
+ public abstract void setDisplayAccessUIDs(SparseArray<IntArray> displayAccessUIDs);
+
+ /**
+ * Check if specified UID's content is present on display and should be granted access to it.
+ *
+ * @param uid UID to be checked.
+ * @param displayId id of the display where presence of the content is checked.
+ * */
+ public abstract boolean isUidPresentOnDisplay(int uid, int displayId);
+
+ /**
* Describes the requested power state of the display.
*
* This object is intended to describe the general characteristics of the
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 3748134..bc4ae6d 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -750,13 +750,14 @@
* @param windowFlags Window layout flags.
* @param overrideConfig override configuration to consider when generating
* context to for resources.
+ * @param displayId Id of the display to show the splash screen at.
*
* @return The starting surface.
*
*/
public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
- int logo, int windowFlags, Configuration overrideConfig);
+ int logo, int windowFlags, Configuration overrideConfig, int displayId);
/**
* Prepare for a window being added to the window manager. You can throw an
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index a2fb9f9..40428c4 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -149,6 +149,7 @@
AppWindowContainerController mWindowContainerController;
final ActivityInfo info; // all about me
final ApplicationInfo appInfo; // information about activity's app
+ final int launchedFromPid; // always the pid who started the activity.
final int launchedFromUid; // always the uid who started the activity.
final String launchedFromPackage; // always the package who started the activity.
final int userId; // Which user is this running for?
@@ -595,7 +596,7 @@
return ResolverActivity.class.getName().equals(realActivity.getClassName());
}
- ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
+ ActivityRecord(ActivityManagerService _service, ProcessRecord _caller, int _launchedFromPid,
int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration,
ActivityRecord _resultTo, String _resultWho, int _reqCode,
@@ -605,6 +606,7 @@
service = _service;
appToken = new Token(this);
info = aInfo;
+ launchedFromPid = _launchedFromPid;
launchedFromUid = _launchedFromUid;
launchedFromPackage = _launchedFromPackage;
userId = UserHandle.getUserId(aInfo.applicationInfo.uid);
@@ -2206,9 +2208,11 @@
throw new XmlPullParserException("restoreActivity resolver error. Intent=" + intent +
" resolvedType=" + resolvedType);
}
- final ActivityRecord r = new ActivityRecord(service, /*caller*/null, launchedFromUid,
- launchedFromPackage, intent, resolvedType, aInfo, service.getConfiguration(),
- null, null, 0, componentSpecified, false, stackSupervisor, null, null, null);
+ final ActivityRecord r = new ActivityRecord(service, null /* caller */,
+ 0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, intent, resolvedType,
+ aInfo, service.getConfiguration(), null /* resultTo */, null /* resultWho */,
+ 0 /* reqCode */, componentSpecified, false /* rootVoiceInteraction */,
+ stackSupervisor, null /* container */, null /* options */, null /* sourceRecord */);
r.persistentState = persistentState;
r.taskDescription = taskDescription;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4df0cb1..4510836 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -105,6 +105,7 @@
import android.service.voice.IVoiceInteractionSession;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -195,6 +196,12 @@
return mActivityContainer.mActivityDisplay;
}
+ @Override
+ void onParentChanged() {
+ super.onParentChanged();
+ mStackSupervisor.updateUIDsPresentOnDisplay();
+ }
+
enum ActivityState {
INITIALIZING,
RESUMED,
@@ -680,6 +687,27 @@
return null;
}
+ boolean isInStackLocked(TaskRecord task) {
+ return mTaskHistory.contains(task);
+ }
+
+ /** Checks if there are tasks with specific UID in the stack. */
+ boolean isUidPresent(int uid) {
+ for (TaskRecord task : mTaskHistory) {
+ if (task.effectiveUid == uid) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Get all UIDs that are present in the stack. */
+ void getPresentUIDs(IntArray presentUIDs) {
+ for (TaskRecord task : mTaskHistory) {
+ presentUIDs.add(task.effectiveUid);
+ }
+ }
+
final boolean updateLRUListLocked(ActivityRecord r) {
final boolean hadit = mLRUActivities.remove(r);
mLRUActivities.add(r);
@@ -4904,13 +4932,13 @@
final boolean toTop = position >= mTaskHistory.size();
final ActivityStack prevStack = preAddTask(task, reason, toTop);
+ mTaskHistory.add(position, task);
task.setStack(this);
if (toTop) {
updateTaskReturnToForTopInsertion(task);
}
- mTaskHistory.add(position, task);
updateTaskMovement(task, toTop);
postAddTask(task, prevStack);
@@ -4927,8 +4955,8 @@
final ActivityRecord topRunningActivity = task.topRunningActivityLocked();
final boolean wasResumed = topRunningActivity == task.getStack().mResumedActivity;
- task.setStack(this);
insertTaskAtPosition(task, index);
+ task.setStack(this);
postAddTask(task, null /* prevStack */);
if (wasResumed) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4fe8939..5ae0102 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -125,6 +125,7 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.VirtualDisplay;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
@@ -154,6 +155,7 @@
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
+import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -380,7 +382,10 @@
/** Mapping from displayId to display current state */
private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>();
- InputManagerInternal mInputManagerInternal;
+ private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
+
+ private DisplayManagerInternal mDisplayManagerInternal;
+ private InputManagerInternal mInputManagerInternal;
/** The chain of tasks in lockTask mode. The current frontmost task is at the top, and tasks
* may be finished until there is only one entry left. If this is empty the system is not
@@ -580,6 +585,7 @@
mDisplayManager =
(DisplayManager)mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
mDisplayManager.registerDisplayListener(this, null);
+ mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
Display[] displays = mDisplayManager.getDisplays();
for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
@@ -1452,7 +1458,7 @@
ActivityRecord resultRecord, ActivityStack resultStack, ActivityOptions options) {
final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
callingUid);
- if (startAnyPerm == PERMISSION_GRANTED) {
+ if (startAnyPerm == PERMISSION_GRANTED) {
return true;
}
final int componentRestriction = getComponentRestrictionForCallingPackage(
@@ -1519,25 +1525,76 @@
// Check if someone tries to launch an activity on a private display with a different
// owner.
final int launchDisplayId = options.getLaunchDisplayId();
- if (launchDisplayId != INVALID_DISPLAY) {
- final ActivityDisplay activityDisplay = mActivityDisplays.get(launchDisplayId);
- if (activityDisplay != null
- && (activityDisplay.mDisplay.getFlags() & FLAG_PRIVATE) != 0) {
- if (activityDisplay.mDisplay.getOwnerUid() != callingUid) {
- final String msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ") with launchDisplayId="
- + launchDisplayId;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
- }
+ if (launchDisplayId != INVALID_DISPLAY
+ && !isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, launchDisplayId)) {
+ final String msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with launchDisplayId="
+ + launchDisplayId;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
}
}
return true;
}
+ /** Check if caller is allowed to launch activities on specified display. */
+ boolean isCallerAllowedToLaunchOnDisplay(int callingPid, int callingUid, int launchDisplayId) {
+ if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check: displayId=" + launchDisplayId
+ + " callingPid=" + callingPid + " callingUid=" + callingUid);
+
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(launchDisplayId);
+ if (activityDisplay == null) {
+ Slog.w(TAG, "Launch on display check: display not found");
+ return false;
+ }
+
+ if (!activityDisplay.isPrivate()) {
+ // Anyone can launch on a public display.
+ if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+ + " allow launch on public display");
+ return true;
+ }
+
+ // Check if the caller is the owner of the display.
+ if (activityDisplay.mDisplay.getOwnerUid() == callingUid) {
+ if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+ + " allow launch for owner of the display");
+ return true;
+ }
+
+ // Check if caller is present on display
+ if (activityDisplay.isUidPresent(callingUid)) {
+ if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+ + " allow launch for caller present on the display");
+ return true;
+ }
+
+ // Check if the caller can launch anything.
+ final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
+ callingUid);
+ if (startAnyPerm == PERMISSION_GRANTED) {
+ if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+ + " allow launch any on display");
+ return true;
+ }
+
+ Slog.w(TAG, "Launch on display check: denied");
+ return false;
+ }
+
+ /** Update lists of UIDs that are present on displays and have access to them. */
+ void updateUIDsPresentOnDisplay() {
+ mDisplayAccessUIDs.clear();
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
+ mDisplayAccessUIDs.append(activityDisplay.mDisplayId, activityDisplay.getPresentUIDs());
+ }
+ // Store updated lists in DisplayManager. Callers from outside of AM should get them there.
+ mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
+ }
+
UserInfo getUserInfo(int userId) {
final long identity = Binder.clearCallingIdentity();
try {
@@ -4568,6 +4625,9 @@
ActivityRecord mVisibleBehindActivity;
+ /** Array of all UIDs that are present on the display. */
+ private IntArray mDisplayAccessUIDs = new IntArray();
+
ActivityDisplay() {
}
@@ -4630,6 +4690,28 @@
protected ConfigurationContainer getParent() {
return ActivityStackSupervisor.this;
}
+
+ boolean isPrivate() {
+ return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
+ }
+
+ boolean isUidPresent(int uid) {
+ for (ActivityStack stack : mStacks) {
+ if (stack.isUidPresent(uid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Update and get all UIDs that are present on the display and have access to it. */
+ private IntArray getPresentUIDs() {
+ mDisplayAccessUIDs.clear();
+ for (ActivityStack stack : mStacks) {
+ stack.getPresentUIDs(mDisplayAccessUIDs);
+ }
+ return mDisplayAccessUIDs;
+ }
}
class VirtualActivityDisplay extends ActivityDisplay {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 2634385..c064cb0 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -476,10 +476,10 @@
aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
}
- ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
- intent, resolvedType, aInfo, mService.getGlobalConfiguration(), resultRecord,
- resultWho, requestCode, componentSpecified, voiceSession != null, mSupervisor,
- container, options, sourceRecord);
+ ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
+ callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
+ resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
+ mSupervisor, container, options, sourceRecord);
if (outActivity != null) {
outActivity[0] = r;
}
@@ -1896,16 +1896,14 @@
final ActivityStack currentStack = task != null ? task.getStack() : null;
if (currentStack != null) {
- if (currentStack.isOnHomeDisplay()) {
- if (mSupervisor.mFocusedStack != currentStack) {
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Setting " + "focused stack to r=" + r
- + " task=" + task);
- } else {
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Focused stack already="
- + mSupervisor.mFocusedStack);
- }
+ if (mSupervisor.mFocusedStack != currentStack) {
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+ "computeStackFocus: Setting " + "focused stack to r=" + r
+ + " task=" + task);
+ } else {
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+ "computeStackFocus: Focused stack already="
+ + mSupervisor.mFocusedStack);
}
return currentStack;
}
@@ -1922,13 +1920,7 @@
// Same also applies to dynamic stacks, as they behave similar to fullscreen stack.
// If the freeform or docked stack has focus, and the activity to be launched is resizeable,
// we can also put it in the focused stack.
- final int focusedStackId = mSupervisor.mFocusedStack.mStackId;
- final boolean canUseFocusedStack = focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
- || (focusedStackId == DOCKED_STACK_ID && r.canGoInDockedStack())
- || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.isResizeableOrForced())
- || isDynamicStack(focusedStackId);
- if (canUseFocusedStack && (!newTask
- || mSupervisor.mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
+ if (canLaunchIntoFocusedStack(r, newTask)) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Have a focused stack=" + mSupervisor.mFocusedStack);
return mSupervisor.mFocusedStack;
@@ -1955,6 +1947,36 @@
return stack;
}
+ /** Check if provided activity record can launch in currently focused stack. */
+ private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
+ // The fullscreen stack can contain any task regardless of if the task is resizeable
+ // or not. So, we let the task go in the fullscreen task if it is the focus stack.
+ // Same also applies to dynamic stacks, as they behave similar to fullscreen stack.
+ // If the freeform or docked stack has focus, and the activity to be launched is resizeable,
+ // we can also put it in the focused stack.
+ final ActivityStack focusedStack = mSupervisor.mFocusedStack;
+ final int focusedStackId = mSupervisor.mFocusedStack.mStackId;
+ final boolean canUseFocusedStack;
+ switch (focusedStackId) {
+ case FULLSCREEN_WORKSPACE_STACK_ID:
+ canUseFocusedStack = true;
+ break;
+ case DOCKED_STACK_ID:
+ canUseFocusedStack = r.canGoInDockedStack();
+ break;
+ case FREEFORM_WORKSPACE_STACK_ID:
+ canUseFocusedStack = r.isResizeableOrForced();
+ break;
+ default:
+ canUseFocusedStack = isDynamicStack(focusedStackId)
+ && mSupervisor.isCallerAllowedToLaunchOnDisplay(r.launchedFromPid,
+ r.launchedFromUid, focusedStack.mDisplayId);
+ }
+
+ return canUseFocusedStack
+ && (!newTask || focusedStack.mActivityContainer.isEligibleForNewTasks());
+ }
+
private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, TaskRecord task,
ActivityOptions aOptions) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index a72a958..6f984a5c 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -741,8 +741,14 @@
return mStack;
}
- /** Must be used for setting parent stack because it performs configuration updates. */
+ /**
+ * Must be used for setting parent stack because it performs configuration updates.
+ * Must be called after adding task as a child to the stack.
+ */
void setStack(ActivityStack stack) {
+ if (stack != null && !stack.isInStackLocked(this)) {
+ throw new IllegalStateException("Task must be added as a Stack child first.");
+ }
mStack = stack;
onParentChanged();
}
@@ -769,6 +775,12 @@
return mStack;
}
+ @Override
+ void onParentChanged() {
+ super.onParentChanged();
+ mService.mStackSupervisor.updateUIDsPresentOnDisplay();
+ }
+
// Close up recents linked list.
void closeRecentsChain() {
if (mPrevAffiliate != null) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9c762cc..cd07793 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -48,6 +48,7 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.text.TextUtils;
+import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -230,6 +231,9 @@
// intended for use inside of the requestGlobalDisplayStateInternal function.
private final ArrayList<Runnable> mTempDisplayStateWorkQueue = new ArrayList<Runnable>();
+ // Lists of UIDs that are present on the displays. Maps displayId -> array of UIDs.
+ private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
+
public DisplayManagerService(Context context) {
super(context);
mContext = context;
@@ -394,7 +398,8 @@
LogicalDisplay display = mLogicalDisplays.get(displayId);
if (display != null) {
DisplayInfo info = display.getDisplayInfoLocked();
- if (info.hasAccess(callingUid)) {
+ if (info.hasAccess(callingUid)
+ || isUidPresentOnDisplayInternal(callingUid, displayId)) {
return info;
}
}
@@ -937,6 +942,25 @@
}
}
+ // Updates the lists of UIDs that are present on displays.
+ private void setDisplayAccessUIDsInternal(SparseArray<IntArray> newDisplayAccessUIDs) {
+ synchronized (mSyncRoot) {
+ mDisplayAccessUIDs.clear();
+ for (int i = newDisplayAccessUIDs.size() - 1; i >= 0; i--) {
+ mDisplayAccessUIDs.append(newDisplayAccessUIDs.keyAt(i),
+ newDisplayAccessUIDs.valueAt(i));
+ }
+ }
+ }
+
+ // Checks if provided UID's content is present on the display and UID has access to it.
+ private boolean isUidPresentOnDisplayInternal(int uid, int displayId) {
+ synchronized (mSyncRoot) {
+ final IntArray displayUIDs = mDisplayAccessUIDs.get(displayId);
+ return displayUIDs != null && displayUIDs.indexOf(uid) != -1;
+ }
+ }
+
private void clearViewportsLocked() {
mDefaultViewport.valid = false;
mExternalTouchViewport.valid = false;
@@ -1647,5 +1671,15 @@
public void setDisplayOffsets(int displayId, int x, int y) {
setDisplayOffsetsInternal(displayId, x, y);
}
+
+ @Override
+ public void setDisplayAccessUIDs(SparseArray<IntArray> newDisplayAccessUIDs) {
+ setDisplayAccessUIDsInternal(newDisplayAccessUIDs);
+ }
+
+ @Override
+ public boolean isUidPresentOnDisplay(int uid, int displayId) {
+ return isUidPresentOnDisplayInternal(uid, displayId);
+ }
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 32b8c9b..c25ad46 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -19,6 +19,8 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.content.Context.DISPLAY_SERVICE;
+import static android.content.Context.WINDOW_SERVICE;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -140,6 +142,7 @@
import android.database.ContentObserver;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
@@ -2254,8 +2257,7 @@
}
lp.format = PixelFormat.TRANSLUCENT;
lp.setTitle("PointerLocation");
- WindowManager wm = (WindowManager)
- mContext.getSystemService(Context.WINDOW_SERVICE);
+ WindowManager wm = (WindowManager) mContext.getSystemService(WINDOW_SERVICE);
lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
wm.addView(mPointerLocationView, lp);
mWindowManagerFuncs.registerPointerEventListener(mPointerLocationView);
@@ -2265,7 +2267,7 @@
private void disablePointerLocation() {
if (mPointerLocationView != null) {
mWindowManagerFuncs.unregisterPointerEventListener(mPointerLocationView);
- WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ WindowManager wm = (WindowManager) mContext.getSystemService(WINDOW_SERVICE);
wm.removeView(mPointerLocationView);
mPointerLocationView = null;
}
@@ -2826,7 +2828,7 @@
@Override
public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
- int logo, int windowFlags, Configuration overrideConfig) {
+ int logo, int windowFlags, Configuration overrideConfig, int displayId) {
if (!SHOW_SPLASH_SCREENS) {
return null;
}
@@ -2927,7 +2929,13 @@
params.setTitle("Splash Screen " + packageName);
- wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+ // Obtain proper context to launch on the right display.
+ final Context displayContext = getDisplayContext(context, displayId);
+ if (displayContext == null) {
+ // Can't show splash screen on requested display, so skip showing at all.
+ return null;
+ }
+ wm = (WindowManager) displayContext.getSystemService(WINDOW_SERVICE);
view = win.getDecorView();
if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "
@@ -2957,6 +2965,24 @@
return null;
}
+ /** Obtain proper context for showing splash screen on the provided display. */
+ private Context getDisplayContext(Context context, int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ // The default context fits.
+ return context;
+ }
+
+ final DisplayManager dm = (DisplayManager) context.getSystemService(DISPLAY_SERVICE);
+ final Display targetDisplay = dm.getDisplay(displayId);
+ if (targetDisplay == null) {
+ // Failed to obtain the non-default display where splash screen should be shown,
+ // lets not show at all.
+ return null;
+ }
+
+ return context.createDisplayContext(targetDisplay);
+ }
+
/**
* Preflight adding a window to the system.
*
diff --git a/services/core/java/com/android/server/wm/SplashScreenStartingData.java b/services/core/java/com/android/server/wm/SplashScreenStartingData.java
index ee4209f..4b14f86 100644
--- a/services/core/java/com/android/server/wm/SplashScreenStartingData.java
+++ b/services/core/java/com/android/server/wm/SplashScreenStartingData.java
@@ -54,6 +54,6 @@
StartingSurface createStartingSurface(AppWindowToken atoken) {
return mService.mPolicy.addSplashScreen(atoken.token, mPkg, mTheme, mCompatInfo,
mNonLocalizedLabel, mLabelRes, mIcon, mLogo, mWindowFlags,
- mMergedOverrideConfiguration);
+ mMergedOverrideConfiguration, atoken.getDisplayContent().getDisplayId());
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index dcc0c6f..71edbb7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1115,7 +1115,8 @@
+ displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
- if (!displayContent.hasAccess(session.mUid)) {
+ if (!displayContent.hasAccess(session.mUid)
+ && !mDisplayManagerInternal.isUidPresentOnDisplay(session.mUid, displayId)) {
Slog.w(TAG_WM, "Attempted to add window to a display for which the application "
+ "does not have access: " + displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index c0c8fb0..269b719 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -321,7 +321,7 @@
@Override
public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
- int logo, int windowFlags, Configuration overrideConfig) {
+ int logo, int windowFlags, Configuration overrideConfig, int displayId) {
return null;
}