Move SysUI to top app scheduling if panel expanded

Also fixes a bug which didn't set the correct renderThreadTid
when restarting an app (for example, when SystemUI gets killed).

Bug: 30292998
Change-Id: I420bee8a11d430b1e52ded050536e56a85a48800
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 277348a..aacd5da 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -3010,6 +3010,13 @@
             reply.writeNoException();
             return true;
         }
+        case SET_HAS_TOP_UI: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final boolean hasTopUi = data.readInt() != 0;
+            setHasTopUi(hasTopUi);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -7072,5 +7079,18 @@
         return;
     }
 
+    public void setHasTopUi(boolean hasTopUi)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(hasTopUi ? 1 : 0);
+        mRemote.transact(SET_HAS_TOP_UI, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+        return;
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 4a4202a..d54ce4b 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -661,6 +661,14 @@
     public void setVrThread(int tid) throws RemoteException;
     public void setRenderThread(int tid) throws RemoteException;
 
+    /**
+     * Let's activity manager know whether the calling process is currently showing "top-level" UI
+     * that is not an activity, i.e. windows on the screen the user is currently interacting with.
+     *
+     * @param hasTopUi Whether the calling process has "top-level" UI.
+     */
+    public void setHasTopUi(boolean hasTopUi) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -1051,4 +1059,5 @@
     // Start of N MR1 transactions
     int SET_VR_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 377;
     int SET_RENDER_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 378;
+    int SET_HAS_TOP_UI = IBinder.FIRST_CALL_TRANSACTION + 379;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index 888e19c..5271481 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -16,11 +16,16 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
+import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.os.Trace;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -41,11 +46,16 @@
  */
 public class StatusBarWindowManager implements RemoteInputController.Callback {
 
+    private static final String TAG = "StatusBarWindowManager";
+
     private final Context mContext;
     private final WindowManager mWindowManager;
+    private final IActivityManager mActivityManager;
     private View mStatusBarView;
     private WindowManager.LayoutParams mLp;
     private WindowManager.LayoutParams mLpChanged;
+    private boolean mHasTopUi;
+    private boolean mHasTopUiChanged;
     private int mBarHeight;
     private final boolean mKeyguardScreenRotation;
     private final float mScreenBrightnessDoze;
@@ -54,6 +64,7 @@
     public StatusBarWindowManager(Context context) {
         mContext = context;
         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        mActivityManager = ActivityManagerNative.getDefault();
         mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
         mScreenBrightnessDoze = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_screenBrightnessDoze) / 255f;
@@ -193,9 +204,18 @@
         applyFitsSystemWindows(state);
         applyModalFlag(state);
         applyBrightness(state);
+        applyHasTopUi(state);
         if (mLp.copyFrom(mLpChanged) != 0) {
             mWindowManager.updateViewLayout(mStatusBarView, mLp);
         }
+        if (mHasTopUi != mHasTopUiChanged) {
+            try {
+                mActivityManager.setHasTopUi(mHasTopUiChanged);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to call setHasTopUi", e);
+            }
+            mHasTopUi = mHasTopUiChanged;
+        }
     }
 
     private void applyForceStatusBarVisibleFlag(State state) {
@@ -224,6 +244,10 @@
         }
     }
 
+    private void applyHasTopUi(State state) {
+        mHasTopUiChanged = isExpanded(state);
+    }
+
     public void setKeyguardShowing(boolean showing) {
         mCurrentState.keyguardShowing = showing;
         apply(mCurrentState);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 59e3096..236f083 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -63,6 +63,7 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import android.Manifest;
+import android.Manifest.permission;
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.Activity;
@@ -6280,6 +6281,9 @@
             enqueueUidChangeLocked(uidRec, -1, UidRecord.CHANGE_ACTIVE);
         }
         proc.uidRecord = uidRec;
+
+        // Reset render thread tid if it was already set, so new process can set it again.
+        proc.renderThreadTid = 0;
         uidRec.numProcs++;
         mProcessNames.put(proc.processName, proc.uid, proc);
         if (proc.isolated) {
@@ -12672,6 +12676,42 @@
         }
     }
 
+    @Override
+    public void setHasTopUi(boolean hasTopUi) throws RemoteException {
+        if (checkCallingPermission(permission.INTERNAL_SYSTEM_WINDOW) != PERMISSION_GRANTED) {
+            String msg = "Permission Denial: setHasTopUi() from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid()
+                    + " requires " + permission.INTERNAL_SYSTEM_WINDOW;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+        final int pid = Binder.getCallingPid();
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                boolean changed = false;
+                ProcessRecord pr;
+                synchronized (mPidsSelfLocked) {
+                    pr = mPidsSelfLocked.get(pid);
+                    if (pr == null) {
+                        Slog.w(TAG, "setHasTopUi called on unknown pid: " + pid);
+                        return;
+                    }
+                    if (pr.hasTopUi != hasTopUi) {
+                        pr.hasTopUi = hasTopUi;
+                        changed = true;
+                    }
+                }
+                if (changed) {
+                    updateOomAdjLocked(pr);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
     public final void enterSafeMode() {
         synchronized(this) {
             // It only makes sense to do this before the system is ready
@@ -19148,7 +19188,7 @@
             // facilitate this, here we need to determine whether or not it
             // is currently showing UI.
             app.systemNoUi = true;
-            if (app == TOP_APP) {
+            if (app == TOP_APP || app.hasTopUi) {
                 app.systemNoUi = false;
                 app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
                 app.adjType = "pers-top-activity";
@@ -19177,7 +19217,7 @@
         int procState;
         boolean foregroundActivities = false;
         BroadcastQueue queue;
-        if (app == TOP_APP) {
+        if (app == TOP_APP || app.hasTopUi) {
             // The last app on the list is the foreground app.
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index dad383d..e74b3a3 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -116,6 +116,8 @@
     boolean repForegroundActivities; // Last reported foreground activities.
     boolean systemNoUi;         // This is a system process, but not currently showing UI.
     boolean hasShownUi;         // Has UI been shown in this process since it was started?
+    boolean hasTopUi;           // Is this process currently showing "top-level" UI that is not an
+                                // activity?
     boolean pendingUiClean;     // Want to clean up resources from showing UI?
     boolean hasAboveClient;     // Bound using BIND_ABOVE_CLIENT, so want to be lower
     boolean treatLikeActivity;  // Bound using BIND_TREAT_LIKE_ACTIVITY