From a8f6018d6c2d28313f9550fa8fcb39c2f607e09c Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 1 Sep 2009 19:01:50 -0700 Subject: [PATCH] Work on issue #2079167: Flickering issue across multiple UI This addresses a few parts of the bug: - There was a small issue in the window manager where we could show a window too early before the transition animation starts, which was introduced by the recent wallpaper work. This was the cause of the flicker when starting the dialer for the first time. - There was a much larger problem that has existing forever where moving an application token to the front or back was not synchronized with the application animation transaction. This was the cause of the flicker when hanging up (now that the in-call screen moves to the back instead of closing and we always have a wallpaper visible). The approach to solving this is to have the window manager go ahead and move the app tokens (it must in order to keep in sync with the activity manager), but to delay the actual window movement: perform the movement to front when the animation starts, and to back when it ends. Actually, when the animation ends, we just go and completely rebuild the window list to ensure it is correct, because there can be ways people can add windows while in this intermediate state where they could end up at the wrong place once we do the delayed movement to the front or back. And it is simply reasuring to know that every time we finish a full app transition, we re-evaluate the world and put everything in its proper place. Also included in this change are a few little tweaks to the input system, to perform better logging, and completely ignore input devices that do not have any of our input classes. There is also a little cleanup of evaluating configuration changes to not do more work than needed when an input devices appears or disappears, and to only log a config change message when the config is truly changing. Change-Id: Ifb2db77f8867435121722a6abeb946ec7c3ea9d3 --- libs/ui/EventHub.cpp | 17 +- .../com/android/server/KeyInputQueue.java | 92 ++++--- .../android/server/WindowManagerService.java | 226 ++++++++++++++++-- .../server/am/ActivityManagerService.java | 1 + 4 files changed, 276 insertions(+), 60 deletions(-) diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp index 60c177b7aac20..e39a357598a95 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -781,12 +781,21 @@ int EventHub::close_device(const char *deviceName) if(strcmp(mDevices[i]->path.string(), deviceName) == 0) { //LOGD("remove device %d: %s\n", i, deviceName); device_t* device = mDevices[i]; - int count = mFDCount - i - 1; + + LOGI("Removed device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n", + device->path.string(), device->name.string(), device->id, + mNumDevicesById, mFDCount, mFDs[i].fd, device->classes); + + // Clear this device's entry. int index = (device->id&ID_MASK); mDevicesById[index].device = NULL; + + // Close the file descriptor and compact the fd array. close(mFDs[i].fd); + int count = mFDCount - i - 1; memmove(mDevices + i, mDevices + i + 1, sizeof(mDevices[0]) * count); memmove(mFDs + i, mFDs + i + 1, sizeof(mFDs[0]) * count); + mFDCount--; #ifdef EV_SW for (int j=0; jnext = mClosingDevices; mClosingDevices = device; - mFDCount--; - uint32_t publicID; if (device->id == mFirstKeyboardId) { LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", @@ -817,7 +824,7 @@ int EventHub::close_device(const char *deviceName) return 0; } } - LOGE("remote device: %s not found\n", deviceName); + LOGE("remove device: %s not found\n", deviceName); return -1; } @@ -832,7 +839,7 @@ int EventHub::read_notify(int nfd) int event_pos = 0; struct inotify_event *event; -LOGD("EventHub::read_notify nfd: %d\n", nfd); + LOGV("EventHub::read_notify nfd: %d\n", nfd); res = read(nfd, event_buf, sizeof(event_buf)); if(res < (int)sizeof(*event)) { if(errno == EINTR) diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java index 7ca12f211da23..244e13679bd0c 100644 --- a/services/java/com/android/server/KeyInputQueue.java +++ b/services/java/com/android/server/KeyInputQueue.java @@ -54,6 +54,7 @@ public abstract class KeyInputQueue { private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; final SparseArray mDevices = new SparseArray(); + final SparseArray mIgnoredDevices = new SparseArray(); final ArrayList mVirtualKeys = new ArrayList(); final HapticFeedbackCallback mHapticFeedbackCallback; @@ -391,26 +392,50 @@ public abstract class KeyInputQueue { if (ev.type == RawInputEvent.EV_DEVICE_ADDED) { synchronized (mFirst) { di = newInputDevice(ev.deviceId); - mDevices.put(ev.deviceId, di); - if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) { - readVirtualKeys(di.name); + if (di.classes != 0) { + // If this device is some kind of input class, + // we care about it. + mDevices.put(ev.deviceId, di); + if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) { + readVirtualKeys(di.name); + } + // The configuration may have changed because + // of this device. + configChanged = true; + } else { + // We won't do anything with this device. + mIgnoredDevices.put(ev.deviceId, di); + Log.i(TAG, "Ignoring non-input device: id=0x" + + Integer.toHexString(di.id) + + ", name=" + di.name); } - configChanged = true; } } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) { synchronized (mFirst) { - Log.i(TAG, "Device removed: id=0x" - + Integer.toHexString(ev.deviceId)); + if (false) { + Log.i(TAG, "Device removed: id=0x" + + Integer.toHexString(ev.deviceId)); + } di = mDevices.get(ev.deviceId); if (di != null) { mDevices.delete(ev.deviceId); + // The configuration may have changed because + // of this device. configChanged = true; + } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) { + mIgnoredDevices.remove(ev.deviceId); } else { - Log.w(TAG, "Bad device id: " + ev.deviceId); + Log.w(TAG, "Removing bad device id: " + + Integer.toHexString(ev.deviceId)); + continue; } } } else { di = getInputDevice(ev.deviceId); + if (di == null) { + // This may be some junk from an ignored device. + continue; + } // first crack at it send = preprocessEvent(di, ev); @@ -422,10 +447,6 @@ public abstract class KeyInputQueue { } } - if (di == null) { - continue; - } - if (configChanged) { synchronized (mFirst) { addLocked(di, System.nanoTime(), 0, @@ -1056,28 +1077,33 @@ public abstract class KeyInputQueue { private InputDevice newInputDevice(int deviceId) { int classes = getDeviceClasses(deviceId); String name = getDeviceName(deviceId); - Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId) - + ", name=" + name - + ", classes=" + Integer.toHexString(classes)); - InputDevice.AbsoluteInfo absX; - InputDevice.AbsoluteInfo absY; - InputDevice.AbsoluteInfo absPressure; - InputDevice.AbsoluteInfo absSize; - if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) { - absX = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_MT_POSITION_X, "X"); - absY = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_MT_POSITION_Y, "Y"); - absPressure = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure"); - absSize = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size"); - } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) { - absX = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_X, "X"); - absY = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_Y, "Y"); - absPressure = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_PRESSURE, "Pressure"); - absSize = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_TOOL_WIDTH, "Size"); - } else { - absX = null; - absY = null; - absPressure = null; - absSize = null; + InputDevice.AbsoluteInfo absX = null; + InputDevice.AbsoluteInfo absY = null; + InputDevice.AbsoluteInfo absPressure = null; + InputDevice.AbsoluteInfo absSize = null; + if (classes != 0) { + Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId) + + ", name=" + name + + ", classes=" + Integer.toHexString(classes)); + if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) { + absX = loadAbsoluteInfo(deviceId, + RawInputEvent.ABS_MT_POSITION_X, "X"); + absY = loadAbsoluteInfo(deviceId, + RawInputEvent.ABS_MT_POSITION_Y, "Y"); + absPressure = loadAbsoluteInfo(deviceId, + RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure"); + absSize = loadAbsoluteInfo(deviceId, + RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size"); + } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) { + absX = loadAbsoluteInfo(deviceId, + RawInputEvent.ABS_X, "X"); + absY = loadAbsoluteInfo(deviceId, + RawInputEvent.ABS_Y, "Y"); + absPressure = loadAbsoluteInfo(deviceId, + RawInputEvent.ABS_PRESSURE, "Pressure"); + absSize = loadAbsoluteInfo(deviceId, + RawInputEvent.ABS_TOOL_WIDTH, "Size"); + } } return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 81e0136169cb6..c7c748f5ac409 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -375,11 +375,14 @@ public class WindowManagerService extends IWindowManager.Stub // made visible or hidden at the next transition. int mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE; boolean mAppTransitionReady = false; + boolean mAppTransitionRunning = false; boolean mAppTransitionTimeout = false; boolean mStartingIconInTransition = false; boolean mSkipAppTransitionAnimation = false; final ArrayList mOpeningApps = new ArrayList(); final ArrayList mClosingApps = new ArrayList(); + final ArrayList mToTopApps = new ArrayList(); + final ArrayList mToBottomApps = new ArrayList(); //flag to detect fat touch events boolean mFatTouch = false; @@ -690,7 +693,11 @@ public class WindowManagerService extends IWindowManager.Stub i--; break; } - if (t.windows.size() > 0) { + + // We haven't reached the token yet; if this token + // is not going to the bottom and has windows, we can + // use it as an anchor for when we do reach the token. + if (!t.sendingToBottom && t.windows.size() > 0) { pos = t.windows.get(0); } } @@ -712,6 +719,8 @@ public class WindowManagerService extends IWindowManager.Stub } placeWindowBefore(pos, win); } else { + // Continue looking down until we find the first + // token that has windows. while (i >= 0) { AppWindowToken t = mAppTokens.get(i); final int NW = t.windows.size(); @@ -2760,7 +2769,6 @@ public class WindowManagerService extends IWindowManager.Stub mTempConfiguration.setToDefaults(); if (computeNewConfigurationLocked(mTempConfiguration)) { if (appConfig.diff(mTempConfiguration) != 0) { - Log.i(TAG, "Config changed: " + mTempConfiguration); return new Configuration(mTempConfiguration); } } @@ -3198,12 +3206,14 @@ public class WindowManagerService extends IWindowManager.Stub wtoken.setDummyAnimation(); mOpeningApps.remove(wtoken); mClosingApps.remove(wtoken); + wtoken.waitingToShow = wtoken.waitingToHide = false; wtoken.inPendingTransaction = true; if (visible) { mOpeningApps.add(wtoken); wtoken.allDrawn = false; wtoken.startingDisplayed = false; wtoken.startingMoved = false; + wtoken.waitingToShow = true; if (wtoken.clientHidden) { // In the case where we are making an app visible @@ -3217,6 +3227,7 @@ public class WindowManagerService extends IWindowManager.Stub } } else { mClosingApps.add(wtoken); + wtoken.waitingToHide = true; } return; } @@ -3351,10 +3362,12 @@ public class WindowManagerService extends IWindowManager.Stub delayed = setTokenVisibilityLocked(wtoken, null, false, WindowManagerPolicy.TRANSIT_NONE, true); wtoken.inPendingTransaction = false; mOpeningApps.remove(wtoken); + wtoken.waitingToShow = false; if (mClosingApps.contains(wtoken)) { delayed = true; } else if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { mClosingApps.add(wtoken); + wtoken.waitingToHide = true; delayed = true; } if (DEBUG_APP_TRANSITIONS) Log.v( @@ -3441,6 +3454,12 @@ public class WindowManagerService extends IWindowManager.Stub final AppWindowToken wtoken = mAppTokens.get(tokenPos-1); if (DEBUG_REORDER) Log.v(TAG, "Looking for lower windows @ " + tokenPos + " -- " + wtoken.token); + if (wtoken.sendingToBottom) { + if (DEBUG_REORDER) Log.v(TAG, + "Skipping token -- currently sending to bottom"); + tokenPos--; + continue; + } int i = wtoken.windows.size(); while (i > 0) { i--; @@ -3449,7 +3468,7 @@ public class WindowManagerService extends IWindowManager.Stub while (j > 0) { j--; WindowState cwin = (WindowState)win.mChildWindows.get(j); - if (cwin.mSubLayer >= 0 ) { + if (cwin.mSubLayer >= 0) { for (int pos=NW-1; pos>=0; pos--) { if (mWindows.get(pos) == cwin) { if (DEBUG_REORDER) Log.v(TAG, @@ -3553,6 +3572,26 @@ public class WindowManagerService extends IWindowManager.Stub } } + private void moveAppWindowsLocked(AppWindowToken wtoken, int tokenPos, + boolean updateFocusAndLayout) { + // First remove all of the windows from the list. + tmpRemoveAppWindowsLocked(wtoken); + + // Where to start adding? + int pos = findWindowOffsetLocked(tokenPos); + + // And now add them back at the correct place. + pos = reAddAppWindowsLocked(pos, wtoken); + + if (updateFocusAndLayout) { + if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) { + assignLayersLocked(); + } + mLayoutNeeded = true; + performLayoutAndPlaceSurfacesLocked(); + } + } + private void moveAppWindowsLocked(List tokens, int tokenPos) { // First remove all of the windows from the list. final int N = tokens.size(); @@ -3575,7 +3614,9 @@ public class WindowManagerService extends IWindowManager.Stub } } - updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); + if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) { + assignLayersLocked(); + } mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); @@ -3596,9 +3637,19 @@ public class WindowManagerService extends IWindowManager.Stub AppWindowToken wt = findAppWindowToken(tokens.get(i)); if (wt != null) { mAppTokens.add(wt); + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + mToTopApps.remove(wt); + mToBottomApps.remove(wt); + mToTopApps.add(wt); + wt.sendingToBottom = false; + wt.sendingToTop = true; + } } } - moveAppWindowsLocked(tokens, mAppTokens.size()); + + if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) { + moveAppWindowsLocked(tokens, mAppTokens.size()); + } } Binder.restoreCallingIdentity(origId); } @@ -3618,10 +3669,20 @@ public class WindowManagerService extends IWindowManager.Stub AppWindowToken wt = findAppWindowToken(tokens.get(i)); if (wt != null) { mAppTokens.add(pos, wt); + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + mToTopApps.remove(wt); + mToBottomApps.remove(wt); + mToBottomApps.add(i, wt); + wt.sendingToTop = false; + wt.sendingToBottom = true; + } pos++; } } - moveAppWindowsLocked(tokens, 0); + + if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) { + moveAppWindowsLocked(tokens, 0); + } } Binder.restoreCallingIdentity(origId); } @@ -4253,19 +4314,6 @@ public class WindowManagerService extends IWindowManager.Stub if (!computeNewConfigurationLocked(config)) { return null; } - Log.i(TAG, "Config changed: " + config); - long now = SystemClock.uptimeMillis(); - //Log.i(TAG, "Config changing, gc pending: " + mFreezeGcPending + ", now " + now); - if (mFreezeGcPending != 0) { - if (now > (mFreezeGcPending+1000)) { - //Log.i(TAG, "Gc! " + now + " > " + (mFreezeGcPending+1000)); - mH.removeMessages(H.FORCE_GC); - Runtime.getRuntime().gc(); - mFreezeGcPending = now; - } - } else { - mFreezeGcPending = now; - } return config; } @@ -7213,6 +7261,10 @@ public class WindowManagerService extends IWindowManager.Stub * of a transition that has not yet been started. */ boolean isReadyForDisplay() { + if (mRootToken.waitingToShow && + mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + return false; + } final AppWindowToken atoken = mAppToken; final boolean animating = atoken != null ? (atoken.animation != null) : false; @@ -7547,6 +7599,22 @@ public class WindowManagerService extends IWindowManager.Stub // Temporary for finding which tokens no longer have visible windows. boolean hasVisible; + // Set to true when this token is in a pending transaction where it + // will be shown. + boolean waitingToShow; + + // Set to true when this token is in a pending transaction where it + // will be hidden. + boolean waitingToHide; + + // Set to true when this token is in a pending transaction where its + // windows will be put to the bottom of the list. + boolean sendingToBottom; + + // Set to true when this token is in a pending transaction where its + // windows will be put to the top of the list. + boolean sendingToTop; + WindowToken(IBinder _token, int type, boolean _explicit) { token = _token; windowType = type; @@ -7559,6 +7627,12 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(prefix); pw.print("windowType="); pw.print(windowType); pw.print(" hidden="); pw.print(hidden); pw.print(" hasVisible="); pw.println(hasVisible); + if (waitingToShow || waitingToHide || sendingToBottom || sendingToTop) { + pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow); + pw.print(" waitingToHide="); pw.print(waitingToHide); + pw.print(" sendingToBottom="); pw.print(sendingToBottom); + pw.print(" sendingToTop="); pw.println(sendingToTop); + } } @Override @@ -7868,7 +7942,7 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows); } pw.print(prefix); pw.print("groupId="); pw.print(groupId); - pw.print(" appFullscreen="); pw.println(appFullscreen); + pw.print(" appFullscreen="); pw.print(appFullscreen); pw.print(" requestedOrientation="); pw.println(requestedOrientation); pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested); pw.print(" clientHidden="); pw.print(clientHidden); @@ -8434,6 +8508,50 @@ public class WindowManagerService extends IWindowManager.Stub return win; } + final void rebuildAppWindowListLocked() { + int NW = mWindows.size(); + int i; + + // First remove all existing app windows. + i=0; + while (i < NW) { + if (((WindowState)mWindows.get(i)).mAppToken != null) { + mWindows.remove(i); + NW--; + continue; + } + i++; + } + + // Now go through the app tokens and add the windows back in. + int NT = mAppTokens.size(); + i = 0; + for (int j=0; j= 0) { + break; + } + mWindows.add(i, cwin); + i++; + } + mWindows.add(i, win); + i++; + for (; c 0) { + NN = mAppTokens.size(); + for (i=0; i