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

Commit 3213c965 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Reduce unnecessary invocation of setInputWindowInfo

Usually most fields of InputWindowHandle don't change frequently.
Therefore, only the changed instances need to be updated. That
reduces the overhead of JNI invocation (especially
NativeInputWindowHandle::updateInfo which may be called from
setInputWindowInfo).

There should be no behavior change.

- Add a InputWindowHandle.ChangeDetectionWrapper to wrap the original
  handle. So the changes of its fields can be tracked.
- Make InputApplicationHandle java side immutable. Its content should
  be rarely changed. Then it is easier to compare by instance. This
  might also reduces the race condition of accessing its field from
  InputDispatcher because the instance is different.
- Move some fields that won't change of InputWindowHandle to the
  constructor of WindowState to reduce unnecessary updates.
- When a window cannot receive input, reuse the per-window input
  window handle to populate the disabled info, so there won't be a
  shared instance that its fields always need to be updated.
- Reduce unnecessary Region#translate if the offsets are zero.
- For a simple activity launch, the invocation amount of
  setInputWindowInfo is reduced 90% (from 126 to 11).
- The metrics updateInputWindows_mean of WmPerfTests is reduced 50%+
  (from 0.89ms to 0.38ms on an old mid-end device).

Bug: 168008622
Test: WindowStateTests#testUpdateInputWindowHandle
      WindowInputTests InternalWindowOperationPerfTest
Change-Id: Ief84bbe6e6fa4da5309912059904932ccf775b75
parent f2d1d264
Loading
Loading
Loading
Loading
+8 −4
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.view;

import android.annotation.NonNull;
import android.os.IBinder;

/**
@@ -31,17 +32,20 @@ public final class InputApplicationHandle {
    private long ptr;

    // Application name.
    public String name;
    public final @NonNull String name;

    // Dispatching timeout.
    public long dispatchingTimeoutMillis;
    public final long dispatchingTimeoutMillis;

    public final IBinder token;
    public final @NonNull IBinder token;

    private native void nativeDispose();

    public InputApplicationHandle(IBinder token) {
    public InputApplicationHandle(@NonNull IBinder token, @NonNull String name,
            long dispatchingTimeoutMillis) {
        this.token = token;
        this.name = name;
        this.dispatchingTimeoutMillis = dispatchingTimeoutMillis;
    }

    public InputApplicationHandle(InputApplicationHandle handle) {
+1 −1
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ public final class InputWindowHandle {
    private long ptr;

    // The input application handle.
    public final InputApplicationHandle inputApplicationHandle;
    public InputApplicationHandle inputApplicationHandle;

    // The token associates input data with a window and its input channel. The client input
    // channel and the server input channel will both contain this token.
+12 −10
Original line number Diff line number Diff line
@@ -54,26 +54,28 @@ jobject NativeInputApplicationHandle::getInputApplicationHandleObjLocalRef(JNIEn

bool NativeInputApplicationHandle::updateInfo() {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    jobject obj = env->NewLocalRef(mObjWeak);
    if (!obj) {
    ScopedLocalRef<jobject> obj(env, env->NewLocalRef(mObjWeak));
    if (!obj.get()) {
        return false;
    }
    if (mInfo.token.get() != nullptr) {
        // The java fields are immutable, so it doesn't need to update again.
        return true;
    }

    mInfo.name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, "<null>");
    mInfo.name = getStringField(env, obj.get(), gInputApplicationHandleClassInfo.name, "<null>");

    mInfo.dispatchingTimeoutMillis =
            env->GetLongField(obj, gInputApplicationHandleClassInfo.dispatchingTimeoutMillis);
            env->GetLongField(obj.get(), gInputApplicationHandleClassInfo.dispatchingTimeoutMillis);

    jobject tokenObj = env->GetObjectField(obj,
            gInputApplicationHandleClassInfo.token);
    if (tokenObj) {
        mInfo.token = ibinderForJavaObject(env, tokenObj);
        env->DeleteLocalRef(tokenObj);
    ScopedLocalRef<jobject> tokenObj(env, env->GetObjectField(obj.get(),
            gInputApplicationHandleClassInfo.token));
    if (tokenObj.get()) {
        mInfo.token = ibinderForJavaObject(env, tokenObj.get());
    } else {
        mInfo.token.clear();
    }

    env->DeleteLocalRef(obj);
    return mInfo.token.get() != nullptr;
}

+16 −2
Original line number Diff line number Diff line
@@ -417,7 +417,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    // mOccludesParent field.
    final boolean hasWallpaper;
    // Input application handle used by the input dispatcher.
    final InputApplicationHandle mInputApplicationHandle;
    private InputApplicationHandle mInputApplicationHandle;

    final int launchedFromPid; // always the pid who started the activity.
    final int launchedFromUid; // always the uid who started the activity.
@@ -1506,7 +1506,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        info = aInfo;
        mUserId = UserHandle.getUserId(info.applicationInfo.uid);
        packageName = info.applicationInfo.packageName;
        mInputApplicationHandle = new InputApplicationHandle(appToken);
        intent = _intent;

        // If the class name in the intent doesn't match that of the target, this is probably an
@@ -1693,6 +1692,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        return lockTaskLaunchMode;
    }

    @NonNull InputApplicationHandle getInputApplicationHandle(boolean update) {
        if (mInputApplicationHandle == null) {
            mInputApplicationHandle = new InputApplicationHandle(appToken, toString(),
                    mInputDispatchingTimeoutMillis);
        } else if (update) {
            final String name = toString();
            if (mInputDispatchingTimeoutMillis != mInputApplicationHandle.dispatchingTimeoutMillis
                    || !name.equals(mInputApplicationHandle.name)) {
                mInputApplicationHandle = new InputApplicationHandle(appToken, name,
                        mInputDispatchingTimeoutMillis);
            }
        }
        return mInputApplicationHandle;
    }

    @Override
    ActivityRecord asActivityRecord() {
        // I am an activity record!
+2 −3
Original line number Diff line number Diff line
@@ -277,9 +277,8 @@ class DragState {
            mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
                    mService.mH.getLooper(), mDragDropController);

            mDragApplicationHandle = new InputApplicationHandle(new Binder());
            mDragApplicationHandle.name = "drag";
            mDragApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
            mDragApplicationHandle = new InputApplicationHandle(new Binder(), "drag",
                    DEFAULT_DISPATCHING_TIMEOUT_MILLIS);

            mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
                    display.getDisplayId());
Loading