Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 519e024d authored by Jeff Brown's avatar Jeff Brown
Browse files

Make input dispatcher only ANR for foreground windows.

Redesigned the input dispatcher's ANR timeout mechanism so it is much
closer to Froyo's policy.  ANR is only ever signalled if the dispatcher
is waiting on a window to finish processing its previous event(s) and
there is new pending input.

In the old code, we tracked the dispatch timeout separately for each
input channel.  This was somewhat complicated and also resulted in the
situation where applications could ANR long after the user had pushed
them into the background.

Change-Id: I666ecada0952d4b95f1d67b9f733842b745c7f4b
parent 3fd5fa4c
Loading
Loading
Loading
Loading
+32 −89
Original line number Diff line number Diff line
@@ -81,9 +81,8 @@ enum {
 */
struct InputTarget {
    enum {
        /* This flag indicates that subsequent event delivery should be held until the
         * current event is delivered to this target or a timeout occurs. */
        FLAG_SYNC = 0x01,
        /* This flag indicates that the event is being delivered to a foreground application. */
        FLAG_FOREGROUND = 0x01,

        /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside
         * of the area of this target and so should instead be delivered as an
@@ -109,12 +108,6 @@ struct InputTarget {
    // Flags for the input target.
    int32_t flags;

    // The timeout for event delivery to this target in nanoseconds, or -1 to wait indefinitely.
    nsecs_t timeout;

    // The time already spent waiting for this target in nanoseconds, or 0 if none.
    nsecs_t timeSpentWaitingForApplication;

    // The x and y offset to add to a MotionEvent as it is delivered.
    // (ignored for KeyEvents)
    float xOffset, yOffset;
@@ -190,6 +183,7 @@ struct InputWindow {
    };

    sp<InputChannel> inputChannel;
    String8 name;
    int32_t layoutParamsFlags;
    int32_t layoutParamsType;
    nsecs_t dispatchingTimeout;
@@ -206,9 +200,11 @@ struct InputWindow {
    int32_t touchableAreaRight;
    int32_t touchableAreaBottom;
    bool visible;
    bool canReceiveKeys;
    bool hasFocus;
    bool hasWallpaper;
    bool paused;
    int32_t layer;
    int32_t ownerPid;
    int32_t ownerUid;

@@ -257,18 +253,12 @@ public:

    /* Notifies the system that an application is not responding.
     * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
    virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
    virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
            const sp<InputChannel>& inputChannel) = 0;

    /* Notifies the system that an input channel is unrecoverably broken. */
    virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) = 0;

    /* Notifies the system that an input channel is not responding.
     * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
    virtual nsecs_t notifyInputChannelANR(const sp<InputChannel>& inputChannel) = 0;

    /* Notifies the system that an input channel recovered from ANR. */
    virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0;

    /* Gets the key repeat initial timeout or -1 if automatic key repeating is disabled. */
    virtual nsecs_t getKeyRepeatTimeout() = 0;

@@ -361,16 +351,6 @@ public:
     */
    virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;

    /* Preempts input dispatch in progress by making pending synchronous
     * dispatches asynchronous instead.  This method is generally called during a focus
     * transition from one application to the next so as to enable the new application
     * to start receiving input as soon as possible without having to wait for the
     * old application to finish up.
     *
     * This method may be called on any thread (usually by the input manager).
     */
    virtual void preemptInputDispatch() = 0;

    /* Registers or unregister input channels that may be used as targets for input events.
     * If monitor is true, the channel will receive a copy of all input events.
     *
@@ -424,7 +404,6 @@ public:
    virtual void setInputWindows(const Vector<InputWindow>& inputWindows);
    virtual void setFocusedApplication(const InputApplication* inputApplication);
    virtual void setInputDispatchMode(bool enabled, bool frozen);
    virtual void preemptInputDispatch();

    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor);
    virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
@@ -454,7 +433,7 @@ private:
        int32_t injectorUid;      // -1 if not injected

        bool dispatchInProgress; // initially false, set to true while dispatching
        int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress
        int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress

        inline bool isInjected() { return injectorPid >= 0; }

@@ -522,7 +501,6 @@ private:
        int32_t targetFlags;
        float xOffset;
        float yOffset;
        nsecs_t timeout;

        // True if dispatch has started.
        bool inProgress;
@@ -540,12 +518,8 @@ private:
        //   will be set to NULL.
        MotionSample* tailMotionSample;

        inline bool isSyncTarget() const {
            return targetFlags & InputTarget::FLAG_SYNC;
        }

        inline void preemptSyncTarget() {
            targetFlags &= ~ InputTarget::FLAG_SYNC;
        inline bool hasForegroundTarget() const {
            return targetFlags & InputTarget::FLAG_FOREGROUND;
        }
    };

@@ -628,6 +602,8 @@ private:
            dequeue(first);
            return first;
        }

        uint32_t count() const;
    };

    /* Allocates queue entries and performs reference counting as needed. */
@@ -647,7 +623,7 @@ private:
                nsecs_t downTime, uint32_t pointerCount,
                const int32_t* pointerIds, const PointerCoords* pointerCoords);
        DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry,
                int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout);
                int32_t targetFlags, float xOffset, float yOffset);
        CommandEntry* obtainCommandEntry(Command command);

        void releaseEventEntry(EventEntry* entry);
@@ -761,8 +737,6 @@ private:
            STATUS_NORMAL,
            // An unrecoverable communication error has occurred.
            STATUS_BROKEN,
            // The client is not responding.
            STATUS_NOT_RESPONDING,
            // The input channel has been unregistered.
            STATUS_ZOMBIE
        };
@@ -772,11 +746,9 @@ private:
        InputPublisher inputPublisher;
        InputState inputState;
        Queue<DispatchEntry> outboundQueue;
        nsecs_t nextTimeoutTime; // next timeout time (LONG_LONG_MAX if none)

        nsecs_t lastEventTime; // the time when the event was originally captured
        nsecs_t lastDispatchTime; // the time when the last event was dispatched
        nsecs_t lastANRTime; // the time when the last ANR was recorded

        explicit Connection(const sp<InputChannel>& inputChannel);

@@ -788,18 +760,6 @@ private:
        // Returns NULL if not found.
        DispatchEntry* findQueuedDispatchEntryForEvent(const EventEntry* eventEntry) const;

        // Determine whether this connection has a pending synchronous dispatch target.
        // Since there can only ever be at most one such target at a time, if there is one,
        // it must be at the tail because nothing else can be enqueued after it.
        inline bool hasPendingSyncTarget() const {
            return ! outboundQueue.isEmpty() && outboundQueue.tailSentinel.prev->isSyncTarget();
        }

        // Assuming there is a pending sync target, make it async.
        inline void preemptSyncTarget() {
            outboundQueue.tailSentinel.prev->preemptSyncTarget();
        }

        // Gets the time since the current event was originally obtained from the input driver.
        inline double getEventLatencyMillis(nsecs_t currentTime) const {
            return (currentTime - lastEventTime) / 1000000.0;
@@ -810,15 +770,7 @@ private:
            return (currentTime - lastDispatchTime) / 1000000.0;
        }

        // Gets the time since the current event ANR was declared, if applicable.
        inline double getANRLatencyMillis(nsecs_t currentTime) const {
            return (currentTime - lastANRTime) / 1000000.0;
        }

        status_t initialize();

        void setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout);
        void resetTimeout(nsecs_t currentTime);
    };

    sp<InputDispatcherPolicyInterface> mPolicy;
@@ -851,7 +803,7 @@ private:
    // All registered connections mapped by receive pipe file descriptor.
    KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;

    ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel);
    ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);

    // Active connections are connections that have a non-empty outbound queue.
    // We don't use a ref-counted pointer here because we explicitly abort connections
@@ -859,12 +811,6 @@ private:
    // and the connection itself to be deactivated.
    Vector<Connection*> mActiveConnections;

    // List of connections that have timed out.  Only used by dispatchOnce()
    // We don't use a ref-counted pointer here because it is not possible for a connection
    // to be unregistered while processing timed out connections since we hold the lock for
    // the duration.
    Vector<Connection*> mTimedOutConnections;

    // Input channels that will receive a copy of all input events.
    Vector<sp<InputChannel> > mMonitoringChannels;

@@ -877,7 +823,7 @@ private:
    void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);

    Condition mInjectionSyncFinishedCondition;
    void decrementPendingSyncDispatchesLocked(EventEntry* entry);
    void decrementPendingForegroundDispatchesLocked(EventEntry* entry);

    // Throttling state.
    struct ThrottleState {
@@ -951,8 +897,6 @@ private:
    void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry);

    // The input targets that were most recently identified for dispatch.
    // If there is a synchronous event dispatch in progress, the current input targets will
    // remain unchanged until the dispatch has completed or been aborted.
    bool mCurrentInputTargetsValid; // false while targets are being recomputed
    Vector<InputTarget> mCurrentInputTargets;
    int32_t mCurrentInputWindowType;
@@ -975,8 +919,9 @@ private:
    int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
            const InputApplication* application, const InputWindow* window,
            nsecs_t* nextWakeupTime);
    void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout);
    nsecs_t getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(nsecs_t currentTime);
    void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
            const sp<InputChannel>& inputChannel);
    nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime);
    void resetANRTimeoutsLocked();

    int32_t findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
@@ -984,14 +929,16 @@ private:
    int32_t findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
            nsecs_t* nextWakeupTime, InputWindow** outWindow);

    void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
            nsecs_t timeSpentWaitingForApplication);
    void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags);
    void addMonitoringTargetsLocked();
    void pokeUserActivityLocked(nsecs_t eventTime, int32_t windowType, int32_t eventType);
    bool checkInjectionPermission(const InputWindow* window,
            int32_t injectorPid, int32_t injectorUid);
    bool isWindowObscuredLocked(const InputWindow* window);
    bool isWindowFinishedWithPreviousInputLocked(const InputWindow* window);
    void releaseTouchedWindowLocked();
    String8 getApplicationWindowLabelLocked(const InputApplication* application,
            const InputWindow* window);

    // Manage the dispatch cycle for a single connection.
    // These methods are deliberately not Interruptible because doing all of the work
@@ -1000,21 +947,14 @@ private:
    void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
            EventEntry* eventEntry, const InputTarget* inputTarget,
            bool resumeWithAppendedMotionSample);
    void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
            nsecs_t timeSpentWaitingForApplication);
    void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
    void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
    void startNextDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
    void timeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
    void resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
            const sp<Connection>& connection, nsecs_t newTimeout);
    void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
            bool broken);
    void drainOutboundQueueLocked(Connection* connection, DispatchEntry* firstDispatchEntryToDrain);
    void drainOutboundQueueLocked(Connection* connection);
    static int handleReceiveCallback(int receiveFd, int events, void* data);

    // Preempting input dispatch.
    bool preemptInputDispatchInnerLocked();

    // Dump state.
    void dumpDispatchStateLocked(String8& dump);
    void logDispatchStateLocked();
@@ -1027,20 +967,23 @@ private:
    void onDispatchCycleStartedLocked(
            nsecs_t currentTime, const sp<Connection>& connection);
    void onDispatchCycleFinishedLocked(
            nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR);
    void onDispatchCycleANRLocked(
            nsecs_t currentTime, const sp<Connection>& connection);
    void onDispatchCycleBrokenLocked(
            nsecs_t currentTime, const sp<Connection>& connection);
    void onANRLocked(
            nsecs_t currentTime, const InputApplication* application, const InputWindow* window,
            nsecs_t eventTime, nsecs_t waitStartTime);

    // Outbound policy interactions.
    void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry);
    void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
    void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry);
    void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry);
    void doNotifyANRLockedInterruptible(CommandEntry* commandEntry);
    void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry);
    void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry);
    void doTargetsNotReadyTimeoutLockedInterruptible(CommandEntry* commandEntry);

    // Statistics gathering.
    void updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
            int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
};

/* Enqueues and dispatches input events, endlessly. */
+269 −393

File changed.

Preview size limit exceeded, changes collapsed.

+2 −22
Original line number Diff line number Diff line
@@ -24,18 +24,13 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Environment;
import android.os.LocalPowerManager;
import android.os.PowerManager;
import android.os.SystemProperties;
import android.util.Slog;
import android.util.Xml;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.WindowManagerPolicy;

import java.io.BufferedReader;
import java.io.File;
@@ -83,7 +78,6 @@ public class InputManager {
    private static native void nativeSetInputWindows(InputWindow[] windows);
    private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
    private static native void nativeSetFocusedApplication(InputApplication application);
    private static native void nativePreemptInputDispatch();
    private static native InputDevice nativeGetInputDevice(int deviceId);
    private static native int[] nativeGetInputDeviceIds();
    private static native String nativeDump();
@@ -331,10 +325,6 @@ public class InputManager {
        nativeSetFocusedApplication(application);
    }
    
    public void preemptInputDispatch() {
        nativePreemptInputDispatch();
    }
    
    public void setInputDispatchMode(boolean enabled, boolean frozen) {
        nativeSetInputDispatchMode(enabled, frozen);
    }
@@ -397,18 +387,8 @@ public class InputManager {
        }
        
        @SuppressWarnings("unused")
        public long notifyInputChannelANR(InputChannel inputChannel) {
            return mWindowManagerService.mInputMonitor.notifyInputChannelANR(inputChannel);
        }

        @SuppressWarnings("unused")
        public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
            mWindowManagerService.mInputMonitor.notifyInputChannelRecoveredFromANR(inputChannel);
        }
        
        @SuppressWarnings("unused")
        public long notifyANR(Object token) {
            return mWindowManagerService.mInputMonitor.notifyANR(token);
        public long notifyANR(Object token, InputChannel inputChannel) {
            return mWindowManagerService.mInputMonitor.notifyANR(token, inputChannel);
        }
        
        @SuppressWarnings("unused")
+9 −0
Original line number Diff line number Diff line
@@ -27,6 +27,9 @@ public final class InputWindow {
    // The input channel associated with the window.
    public InputChannel inputChannel;
    
    // The window name.
    public String name;
    
    // Window layout params attributes.  (WindowManager.LayoutParams)
    public int layoutParamsFlags;
    public int layoutParamsType;
@@ -55,6 +58,9 @@ public final class InputWindow {
    // Window is visible.
    public boolean visible;
    
    // Window can receive keys.
    public boolean canReceiveKeys;
    
    // Window has focus.
    public boolean hasFocus;
    
@@ -64,6 +70,9 @@ public final class InputWindow {
    // Input event dispatching is paused.
    public boolean paused;
    
    // Window layer.
    public int layer;
    
    // Id of process and user that owns the window.
    public int ownerPid;
    public int ownerUid;
+22 −66
Original line number Diff line number Diff line
@@ -5096,61 +5096,39 @@ public class WindowManagerService extends IWindowManager.Stub
            }
        }
        
        /* Notifies the window manager about an input channel that is not responding.
        /* Notifies the window manager about an application that is not responding.
         * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
         * 
         * Called by the InputManager.
         */
        public long notifyInputChannelANR(InputChannel inputChannel) {
            AppWindowToken token;
        public long notifyANR(Object token, InputChannel inputChannel) {
            AppWindowToken appWindowToken = null;
            if (inputChannel != null) {
                synchronized (mWindowMap) {
                    WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
                if (windowState == null) {
                    return 0; // window is unknown, abort dispatching
                }
                
                    if (windowState != null) {
                        Slog.i(TAG, "Input event dispatching timed out sending to "
                                + windowState.mAttrs.getTitle());
                token = windowState.mAppToken;
                        appWindowToken = windowState.mAppToken;
                    }
            
            return notifyANRInternal(token);
                }
    
        /* Notifies the window manager about an input channel spontaneously recovering from ANR
         * by successfully delivering the event that originally timed out.
         * 
         * Called by the InputManager.
         */
        public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
            // Nothing to do just now.
            // Just wait for the user to dismiss the ANR dialog.
            }
            
        /* Notifies the window manager about an application that is not responding
         * in general rather than with respect to a particular input channel.
         * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
         * 
         * Called by the InputManager.
         */
        public long notifyANR(Object token) {
            AppWindowToken appWindowToken = (AppWindowToken) token;

            if (appWindowToken == null && token != null) {
                appWindowToken = (AppWindowToken) token;
                Slog.i(TAG, "Input event dispatching timed out sending to application "
                        + appWindowToken.stringName);
            return notifyANRInternal(appWindowToken);
            }

        private long notifyANRInternal(AppWindowToken token) {
            if (token != null && token.appToken != null) {
            if (appWindowToken != null && appWindowToken.appToken != null) {
                try {
                    // Notify the activity manager about the timeout and let it decide whether
                    // to abort dispatching or keep waiting.
                    boolean abort = token.appToken.keyDispatchingTimedOut();
                    boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();
                    if (! abort) {
                        // The activity manager declined to abort dispatching.
                        // Wait a bit longer and timeout again later.
                        return token.inputDispatchingTimeoutNanos;
                        return appWindowToken.inputDispatchingTimeoutNanos;
                    }
                } catch (RemoteException ex) {
                }
@@ -5203,13 +5181,16 @@ public class WindowManagerService extends IWindowManager.Stub
                // Add a window to our list of input windows.
                final InputWindow inputWindow = mTempInputWindows.add();
                inputWindow.inputChannel = child.mInputChannel;
                inputWindow.name = child.toString();
                inputWindow.layoutParamsFlags = flags;
                inputWindow.layoutParamsType = type;
                inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
                inputWindow.visible = isVisible;
                inputWindow.canReceiveKeys = child.canReceiveKeys();
                inputWindow.hasFocus = hasFocus;
                inputWindow.hasWallpaper = hasWallpaper;
                inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false;
                inputWindow.layer = child.mLayer;
                inputWindow.ownerPid = child.mSession.mPid;
                inputWindow.ownerUid = child.mSession.mUid;
                
@@ -5302,23 +5283,6 @@ public class WindowManagerService extends IWindowManager.Stub

            if (newWindow != mInputFocus) {
                if (newWindow != null && newWindow.canReceiveKeys()) {
                    // If the new input focus is an error window or appears above the current
                    // input focus, preempt any pending synchronous dispatch so that we can
                    // start delivering events to the new input focus as soon as possible.
                    if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) {
                        if (DEBUG_INPUT) {
                            Slog.v(TAG, "New SYSTEM_ERROR window; resetting state");
                        }
                        preemptInputDispatchLw();
                    } else if (mInputFocus != null && newWindow.mLayer > mInputFocus.mLayer) {
                        if (DEBUG_INPUT) {
                            Slog.v(TAG, "Transferring focus to new window at higher layer: "
                                    + "old win layer=" + mInputFocus.mLayer
                                    + ", new win layer=" + newWindow.mLayer);
                        }
                        preemptInputDispatchLw();
                    }
                    
                    // Displaying a window implicitly causes dispatching to be unpaused.
                    // This is to protect against bugs if someone pauses dispatching but
                    // forgets to resume.
@@ -5330,14 +5294,6 @@ public class WindowManagerService extends IWindowManager.Stub
            }
        }
        
        /* Tells the dispatcher to stop waiting for its current synchronous event targets.
         * Essentially, just makes those dispatches asynchronous so a new dispatch cycle
         * can begin.
         */
        private void preemptInputDispatchLw() {
            mInputManager.preemptInputDispatch();
        }
        
        public void setFocusedAppLw(AppWindowToken newApp) {
            // Focused app has changed.
            if (newApp == null) {
Loading