Reduce system calls of ThreadPriorityBooster

- ThreadLocal is bound to each thread, so we don't need to
  get tid every time.
- By storing whether the priority is changed, we don't need
  to get thread priority to decide whether to restore.

The cost of a simple boost region is reduced over 20%.

Test: atest ThreadPriorityBoosterTest
Bug: 123043091
Change-Id: I634898c1bf0d10de4f53fd18043f64d18fe9082b
diff --git a/services/core/java/com/android/server/ThreadPriorityBooster.java b/services/core/java/com/android/server/ThreadPriorityBooster.java
index f74a4385..dab6bc4 100644
--- a/services/core/java/com/android/server/ThreadPriorityBooster.java
+++ b/services/core/java/com/android/server/ThreadPriorityBooster.java
@@ -26,6 +26,7 @@
 public class ThreadPriorityBooster {
 
     private static final boolean ENABLE_LOCK_GUARD = false;
+    private static final int PRIORITY_NOT_ADJUSTED = Integer.MAX_VALUE;
 
     private volatile int mBoostToPriority;
     private final int mLockGuardIndex;
@@ -42,13 +43,12 @@
     }
 
     public void boost() {
-        final int tid = myTid();
         final PriorityState state = mThreadState.get();
         if (state.regionCounter == 0) {
-            final int prevPriority = getThreadPriority(tid);
-            state.prevPriority = prevPriority;
+            final int prevPriority = getThreadPriority(state.tid);
             if (prevPriority > mBoostToPriority) {
-                setThreadPriority(tid, mBoostToPriority);
+                setThreadPriority(state.tid, mBoostToPriority);
+                state.prevPriority = prevPriority;
             }
         }
         state.regionCounter++;
@@ -60,11 +60,9 @@
     public void reset() {
         final PriorityState state = mThreadState.get();
         state.regionCounter--;
-        if (state.regionCounter == 0) {
-            final int currentPriority = getThreadPriority(myTid());
-            if (state.prevPriority != currentPriority) {
-                setThreadPriority(myTid(), state.prevPriority);
-            }
+        if (state.regionCounter == 0 && state.prevPriority != PRIORITY_NOT_ADJUSTED) {
+            setThreadPriority(state.tid, state.prevPriority);
+            state.prevPriority = PRIORITY_NOT_ADJUSTED;
         }
     }
 
@@ -78,16 +76,16 @@
         // variable immediately.
         mBoostToPriority = priority;
         final PriorityState state = mThreadState.get();
-        final int tid = myTid();
         if (state.regionCounter != 0) {
-            final int prevPriority = getThreadPriority(tid);
+            final int prevPriority = getThreadPriority(state.tid);
             if (prevPriority != priority) {
-                setThreadPriority(tid, priority);
+                setThreadPriority(state.tid, priority);
             }
         }
     }
 
     private static class PriorityState {
+        final int tid = myTid();
 
         /**
          * Acts as counter for number of synchronized region that needs to acquire 'this' as a lock
@@ -99,6 +97,6 @@
         /**
          * The thread's previous priority before boosting.
          */
-        int prevPriority;
+        int prevPriority = PRIORITY_NOT_ADJUSTED;
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/ThreadPriorityBoosterTest.java b/services/tests/servicestests/src/com/android/server/ThreadPriorityBoosterTest.java
new file mode 100644
index 0000000..b4e9ff7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/ThreadPriorityBoosterTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.os.Process.getThreadPriority;
+import static android.os.Process.myTid;
+import static android.os.Process.setThreadPriority;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Process;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link ThreadPriorityBooster}.
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:ThreadPriorityBoosterTest
+ */
+@SmallTest
+@Presubmit
+public class ThreadPriorityBoosterTest {
+    private static final int PRIORITY_BOOST = Process.THREAD_PRIORITY_FOREGROUND;
+    private static final int PRIORITY_BOOST_MORE = Process.THREAD_PRIORITY_DISPLAY;
+
+    private final ThreadPriorityBooster mBooster = new ThreadPriorityBooster(PRIORITY_BOOST,
+            0 /* lockGuardIndex */);
+
+    @Test
+    public void testThreadPriorityBooster() {
+        joinNewThread(() -> {
+            final int origPriority = Process.THREAD_PRIORITY_DEFAULT;
+            setThreadPriority(origPriority);
+
+            boost(() -> {
+                assertThreadPriority(PRIORITY_BOOST);
+                boost(() -> {
+                    // Inside the boost region, the priority should also apply to current thread.
+                    mBooster.setBoostToPriority(PRIORITY_BOOST_MORE);
+                    assertThreadPriority(PRIORITY_BOOST_MORE);
+                });
+                // It is still in the boost region so the set priority should be kept.
+                assertThreadPriority(PRIORITY_BOOST_MORE);
+
+                joinNewThread(() -> boost(() -> assertThreadPriority(PRIORITY_BOOST_MORE)));
+            });
+            // The priority should be restored after leaving the boost region.
+            assertThreadPriority(origPriority);
+
+            // It doesn't apply to current thread because outside of the boost region, but the boost
+            // in other threads will use the set priority.
+            mBooster.setBoostToPriority(PRIORITY_BOOST);
+            joinNewThread(() -> boost(() -> assertThreadPriority(PRIORITY_BOOST)));
+
+            assertThreadPriority(origPriority);
+        });
+    }
+
+    private static void assertThreadPriority(int expectedPriority) {
+        assertEquals(expectedPriority, getThreadPriority(myTid()));
+    }
+
+    private static void joinNewThread(Runnable action) {
+        final Thread thread = new Thread(action);
+        thread.start();
+        try {
+            thread.join();
+        } catch (InterruptedException ignored) {
+        }
+    }
+
+    private void boost(Runnable action) {
+        try {
+            mBooster.boost();
+            action.run();
+        } finally {
+            mBooster.reset();
+        }
+    }
+}