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

Commit e96440fa authored by Michael Chan's avatar Michael Chan Committed by The Android Open Source Project
Browse files

AI 148368: Decrease CPU usage by throttling touch events

  We are decreasing CPU usage at the cost of event latency. Events are queued up
  and released to the app at the specified rate. You can
  experiment with different values via:
  adb shell setprop windowsmgr.max_events_per_sec 35
  The new value is picked up when you let go and retouch the screen. No
  reboot needed.
  Also the following changes were made after profiling:
  - In WindowManagerService, limit the call to userActivity() when we
  have a flood touch events.
  - In PowerManagerService, skip checking of permission if the caller
  is system user.
  - In PowerManagerService, integrated the functionality of gatherState()
  into reactivateWakeLocksLocked(). They loop through the same data
  structure and are called back to back.
  BUG=1692771

Automated import of CL 148368
parent dffbb4db
Loading
Loading
Loading
Loading
+27 −20
Original line number Diff line number Diff line
@@ -496,8 +496,10 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
    }

    public void acquireWakeLock(int flags, IBinder lock, String tag) {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
        int uid = Binder.getCallingUid();
        if (uid != Process.myUid()) {
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
        }
        long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mLocks) {
@@ -554,14 +556,14 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
            // by the current state so we never turn it more on than
            // it already is.
            if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
                reactivateWakeLocksLocked();
                int oldWakeLockState = mWakeLockState;
                mWakeLockState = mLocks.reactivateScreenLocksLocked();
                if (mSpew) {
                    Log.d(TAG, "wakeup here mUserState=0x" + Integer.toHexString(mUserState)
                            + " mLocks.gatherState()=0x"
                            + Integer.toHexString(mLocks.gatherState())
                            + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState));
                            + " mWakeLockState=0x"
                            + Integer.toHexString(mWakeLockState)
                            + " previous wakeLockState=0x" + Integer.toHexString(oldWakeLockState));
                }
                mWakeLockState = mLocks.gatherState();
            } else {
                if (mSpew) {
                    Log.d(TAG, "here mUserState=0x" + Integer.toHexString(mUserState)
@@ -598,7 +600,10 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
    }

    public void releaseWakeLock(IBinder lock) {
        int uid = Binder.getCallingUid();
        if (uid != Process.myUid()) {
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
        }

        synchronized (mLocks) {
            releaseWakeLockLocked(lock, false);
@@ -653,17 +658,6 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
        }
    }

    private void reactivateWakeLocksLocked()
    {
        int N = mLocks.size();
        for (int i=0; i<N; i++) {
            WakeLock wl = mLocks.get(i);
            if (isScreenLock(wl.flags)) {
                mLocks.get(i).activated = true;
            }
        }
    }

    private class PokeLock implements IBinder.DeathRecipient
    {
        PokeLock(int p, IBinder b, String t) {
@@ -1752,8 +1746,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
                        Binder.restoreCallingIdentity(ident);
                    }
                    
                    reactivateWakeLocksLocked();
                    mWakeLockState = mLocks.gatherState();
                    mWakeLockState = mLocks.reactivateScreenLocksLocked();
                    setPowerState(mUserState | mWakeLockState, noChangeLights, true);
                    setTimeoutLocked(time, SCREEN_BRIGHT);
                }
@@ -1944,6 +1937,20 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
            }
            return result;
        }
        
        int reactivateScreenLocksLocked()
        {
            int result = 0;
            int N = this.size();
            for (int i=0; i<N; i++) {
                WakeLock wl = this.get(i);
                if (isScreenLock(wl.flags)) {
                    wl.activated = true;
                    result |= wl.minState;
                }
            }
            return result;
        }
    }

    void setPolicy(WindowManagerPolicy p) {
+76 −8
Original line number Diff line number Diff line
@@ -180,6 +180,25 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
    static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
    static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3;
    
    /** The minimum time between dispatching touch events. */
    int mMinWaitTimeBetweenTouchEvents = 1000 / 35;

    // Last touch event time
    long mLastTouchEventTime = 0;
    
    // Last touch event type
    int mLastTouchEventType = OTHER_EVENT;
    
    // Time to wait before calling useractivity again. This saves CPU usage
    // when we get a flood of touch events.
    static final int MIN_TIME_BETWEEN_USERACTIVITIES = 1000;

    // Last time we call user activity
    long mLastUserActivityCallTime = 0;

    // Last time we updated battery stats 
    long mLastBatteryStatsCallTime = 0;
    
    private static final String SYSTEM_SECURE = "ro.secure";

    /**
@@ -3689,9 +3708,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
    // -------------------------------------------------------------

    private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
        if (targetWin == null ||
                targetWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
            mPowerManager.userActivity(SystemClock.uptimeMillis(), false, eventType);
        long curTime = SystemClock.uptimeMillis();

        if (eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT) {
            if (mLastTouchEventType == eventType &&
                    (curTime - mLastUserActivityCallTime) < MIN_TIME_BETWEEN_USERACTIVITIES) {
                return;
            }
            mLastUserActivityCallTime = curTime;
            mLastTouchEventType = eventType;
        }

        if (targetWin == null
                || targetWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
            mPowerManager.userActivity(curTime, false, eventType, false);
        }
    }

@@ -3759,7 +3789,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
            // events in such a way, since this means the user is moving the
            // pointer without actually pressing down.  All other cases should
            // be atypical, so let's log them.
            if (ev.getAction() != MotionEvent.ACTION_MOVE) {
            if (action != MotionEvent.ACTION_MOVE) {
                Log.w(TAG, "No window to dispatch pointer action " + ev.getAction());
            }
            if (qev != null) {
@@ -3847,6 +3877,38 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
            }
        } //end if target

        // TODO remove once we settle on a value or make it app specific
        if (action == MotionEvent.ACTION_DOWN) {
            int max_events_per_sec = 35;
            try {
                max_events_per_sec = Integer.parseInt(SystemProperties
                        .get("windowsmgr.max_events_per_sec"));
                if (max_events_per_sec < 1) {
                    max_events_per_sec = 35;
                }
            } catch (NumberFormatException e) {
            }
            mMinWaitTimeBetweenTouchEvents = 1000 / max_events_per_sec;
        }

        /*
         * Throttle events to minimize CPU usage when there's a flood of events
         * e.g. constant contact with the screen
         */
        if (action == MotionEvent.ACTION_MOVE) {
            long nextEventTime = mLastTouchEventTime + mMinWaitTimeBetweenTouchEvents;
            long now = SystemClock.uptimeMillis();
            if (now < nextEventTime) {
                try {
                    Thread.sleep(nextEventTime - now);
                } catch (InterruptedException e) {
                }
                mLastTouchEventTime = nextEventTime;
            } else {
                mLastTouchEventTime = now;
            }
        }

        synchronized(mWindowMap) {
            if (qev != null && action == MotionEvent.ACTION_MOVE) {
                mKeyWaiter.bindTargetWindowLocked(target,
@@ -4926,7 +4988,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
                    }
                    if ((actions & WindowManagerPolicy.ACTION_POKE_USER_ACTIVITY) != 0) {
                        mPowerManager.userActivity(event.when, false,
                                LocalPowerManager.BUTTON_EVENT);
                                LocalPowerManager.BUTTON_EVENT, false);
                    }
                    
                    if ((actions & WindowManagerPolicy.ACTION_PASS_TO_USER) != 0) {
@@ -5086,11 +5148,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
                            eventType = LocalPowerManager.OTHER_EVENT;
                        }
                        try {
                            long now = SystemClock.uptimeMillis();

                            if ((now - mLastBatteryStatsCallTime)
                                    >= MIN_TIME_BETWEEN_USERACTIVITIES) {
                                mLastBatteryStatsCallTime = now;
                                mBatteryStats.noteInputEvent();
                            }
                        } catch (RemoteException e) {
                            // Ignore
                        }
                        mPowerManager.userActivity(curTime, false, eventType);
                        mPowerManager.userActivity(curTime, false, eventType, false);
                        switch (ev.classType) {
                            case RawInputEvent.CLASS_KEYBOARD:
                                KeyEvent ke = (KeyEvent)ev.event;