summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java129
1 files changed, 125 insertions, 4 deletions
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 70069c60c330..baf4f140c383 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -21,6 +21,8 @@ import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
import static android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT;
import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
import static android.hardware.display.DisplayManager.EventsMask;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
@@ -43,7 +45,10 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.compat.CompatChanges;
import android.companion.virtual.IVirtualDevice;
@@ -233,6 +238,9 @@ public final class DisplayManagerService extends SystemService {
private final DisplayModeDirector mDisplayModeDirector;
private WindowManagerInternal mWindowManagerInternal;
private InputManagerInternal mInputManagerInternal;
+ private ActivityManagerInternal mActivityManagerInternal;
+ private ActivityManager mActivityManager;
+ private UidImportanceListener mUidImportanceListener = new UidImportanceListener();
private IMediaProjectionManager mProjectionService;
private DeviceStateManagerInternal mDeviceStateManager;
@GuardedBy("mSyncRoot")
@@ -412,6 +420,11 @@ public final class DisplayManagerService extends SystemService {
// May be used outside of the lock but only on the handler thread.
private final ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>();
+ // Pending callback records indexed by calling process uid.
+ // Must be used outside of the lock mSyncRoot and should be selflocked.
+ @GuardedBy("mPendingCallbackSelfLocked")
+ public final SparseArray<PendingCallback> mPendingCallbackSelfLocked = new SparseArray<>();
+
// Temporary viewports, used when sending new viewport information to the
// input system. May be used outside of the lock but only on the handler thread.
private final ArrayList<DisplayViewport> mTempViewports = new ArrayList<>();
@@ -619,11 +632,18 @@ public final class DisplayManagerService extends SystemService {
}
}
- // TODO: Use dependencies or a boot phase
+ /**
+ * The 2nd stage initialization
+ * TODO: Use dependencies or a boot phase
+ */
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void windowManagerAndInputReady() {
synchronized (mSyncRoot) {
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mActivityManager = mContext.getSystemService(ActivityManager.class);
+ mActivityManager.addOnUidImportanceListener(mUidImportanceListener, IMPORTANCE_CACHED);
mDeviceStateManager = LocalServices.getService(DeviceStateManagerInternal.class);
mContext.getSystemService(DeviceStateManager.class).registerCallback(
@@ -825,6 +845,36 @@ public final class DisplayManagerService extends SystemService {
}
}
+ private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
+ @Override
+ public void onUidImportance(int uid, int importance) {
+ synchronized (mPendingCallbackSelfLocked) {
+ if (importance >= IMPORTANCE_GONE) {
+ // Clean up as the app is already gone
+ Slog.d(TAG, "Drop pending events for gone uid " + uid);
+ mPendingCallbackSelfLocked.delete(uid);
+ return;
+ } else if (importance >= IMPORTANCE_CACHED) {
+ // Nothing to do as the app is still in cached mode
+ return;
+ }
+
+ // Do we care about this uid?
+ PendingCallback pendingCallback = mPendingCallbackSelfLocked.get(uid);
+ if (pendingCallback == null) {
+ return;
+ }
+
+ // Send the pending events out when a certain uid becomes non-cached
+ if (DEBUG) {
+ Slog.d(TAG, "Uid " + uid + " becomes " + importance);
+ }
+ pendingCallback.sendPendingDisplayEvent();
+ mPendingCallbackSelfLocked.delete(uid);
+ }
+ }
+ }
+
private class SettingsObserver extends ContentObserver {
SettingsObserver() {
super(mHandler);
@@ -2538,6 +2588,16 @@ public final class DisplayManagerService extends SystemService {
}
}
+ // Check if the target app is in cached mode
+ private boolean isUidCached(int uid) {
+ if (mActivityManagerInternal == null) {
+ return false;
+ }
+ int procState = mActivityManagerInternal.getUidProcessState(uid);
+ int importance = ActivityManager.RunningAppProcessInfo.procStateToImportance(procState);
+ return importance >= IMPORTANCE_CACHED;
+ }
+
// Runs on Handler thread.
// Delivers display event notifications to callbacks.
private void deliverDisplayEvent(int displayId, ArraySet<Integer> uids,
@@ -2561,7 +2621,22 @@ public final class DisplayManagerService extends SystemService {
// After releasing the lock, send the notifications out.
for (int i = 0; i < mTempCallbacks.size(); i++) {
- mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event);
+ CallbackRecord callbackRecord = mTempCallbacks.get(i);
+ final int uid = callbackRecord.mUid;
+ if (isUidCached(uid)) {
+ // For cached apps, save the pending event until it becomes non-cached
+ synchronized (mPendingCallbackSelfLocked) {
+ PendingCallback pendingCallback = mPendingCallbackSelfLocked.get(uid);
+ if (pendingCallback == null) {
+ mPendingCallbackSelfLocked.put(uid,
+ new PendingCallback(callbackRecord, displayId, event));
+ } else {
+ pendingCallback.addDisplayEvent(displayId, event);
+ }
+ }
+ } else {
+ callbackRecord.notifyDisplayEventAsync(displayId, event);
+ }
}
mTempCallbacks.clear();
}
@@ -2997,17 +3072,22 @@ public final class DisplayManagerService extends SystemService {
onCallbackDied(this);
}
- public void notifyDisplayEventAsync(int displayId, @DisplayEvent int event) {
+ /**
+ * @return {@code false} if RemoteException happens; otherwise {@code true} for success.
+ */
+ public boolean notifyDisplayEventAsync(int displayId, @DisplayEvent int event) {
if (!shouldSendEvent(event)) {
- return;
+ return true;
}
try {
mCallback.onDisplayEvent(displayId, event);
+ return true;
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify process "
+ mPid + " that displays changed, assuming it died.", ex);
binderDied();
+ return false;
}
}
@@ -3030,6 +3110,47 @@ public final class DisplayManagerService extends SystemService {
}
}
+ private static final class PendingCallback {
+ private final CallbackRecord mCallbackRecord;
+ private final ArrayList<Pair<Integer, Integer>> mDisplayEvents;
+
+ PendingCallback(CallbackRecord cr, int displayId, int event) {
+ mCallbackRecord = cr;
+ mDisplayEvents = new ArrayList<>();
+ mDisplayEvents.add(new Pair<>(displayId, event));
+ }
+
+ public void addDisplayEvent(int displayId, int event) {
+ // Ignore redundant events. Further optimization is possible by merging adjacent events.
+ Pair<Integer, Integer> last = mDisplayEvents.get(mDisplayEvents.size() - 1);
+ if (last.first == displayId && last.second == event) {
+ Slog.d(TAG,
+ "Ignore redundant display event " + displayId + "/" + event + " to "
+ + mCallbackRecord.mUid + "/" + mCallbackRecord.mPid);
+ return;
+ }
+
+ mDisplayEvents.add(new Pair<>(displayId, event));
+ }
+
+ public void sendPendingDisplayEvent() {
+ for (int i = 0; i < mDisplayEvents.size(); i++) {
+ Pair<Integer, Integer> displayEvent = mDisplayEvents.get(i);
+ if (DEBUG) {
+ Slog.d(TAG, "Send pending display event #" + i + " " + displayEvent.first + "/"
+ + displayEvent.second + " to " + mCallbackRecord.mUid + "/"
+ + mCallbackRecord.mPid);
+ }
+ if (!mCallbackRecord.notifyDisplayEventAsync(displayEvent.first,
+ displayEvent.second)) {
+ Slog.d(TAG, "Drop pending events for dead process " + mCallbackRecord.mPid);
+ break;
+ }
+ }
+ mDisplayEvents.clear();
+ }
+ }
+
@VisibleForTesting
final class BinderService extends IDisplayManager.Stub {
/**